1 - Hallo zusammen :-)

Willkommen bei Sonic Pi! Hoffentlich bist Du gespannt darauf, zu lernen, wie man mit Sonic Pi verrückte Sounds machen kann, genauso wie ich gespannt darauf bin, Dir zu zeigen, wie es geht. Es wird ein Riesenspaß bei dem Du etwas über Musik, Synthese, Programmieren, Komposition, Aufführung und viele andere Dinge lernen wirst.

Moment, wie unhöflich von mir! Ich sollte mich erst einmal vorstellen. Ich bin Sam Aaron der Typ, der Sonic Pi gebaut hat. Du kannst mich unter @samaaron auf Twitter finden. Schau doch mal vorbei. Vielleicht interessierst Du Dich auch für meine Live Coding Performances, wo ich den Sonic Pi Code für meine Musik direkt vor Publikum schreibe.

Bitte schreibe mir, wenn Du irgendwelche Vorschläge oder Ideen zur Verbesserung von Sonic Pi hast – Feedback ist sehr hilfreich. Man kann ja nie wissen, vielleicht ist Deine Idee das nächste große Feature!

Dieses Tutorial ist in Kapitel aufgeteilt, die nach Themen gruppiert sind. Auch wenn ich es so geschrieben habe, dass man damit von Anfang bis zum Ende einfach lernen kann, kannst Du an jeder Stelle einsteigen, ganz wie es Dir gefällt. Wenn Du etwas im Tutorial vermisst, lass es mich wissen, und ich überlege, wie ich das in zukünftige Versionen einbauen kann.

Besonders gut lernt man Live-Coding, wenn man anderen dabei zuschaut. Ich streame meine Musik regelmäßig auf http://youtube.com/samaaron. Schau gerne mal vorbei, sag Hallo und frag mich dort ein wenig aus. :-)

Ok, dann legen wir los …


1.1 - Live-Coding

Mit Sonic Pi kannst Du etwas sehr aufregendes tun: Du kannst Musik aus Code live schreiben und verändern, während sie spielt. Fast so wie mit einer Gitarre. Mit anderen Worten: Du kannst Sonic Pi mit auf die Bühne nehmen und jammen.

Mach mal kurz den Kopf frei

Wir werden in diesem Tutorial in die Details von Sonic Pi eintauchen. Jetzt möchte ich Dir aber erst einmal zeigen, wie das ist, live Musik zu programmieren. Keine Sorge, Du wirst vielleicht nicht sofort alles verstehen. Aber das macht nichts. Genieße kurz diesen Einblick.

Eine Live-Loop

Lass uns loslegen. Kopiere den folgenden Code nach oben in einen leeren Puffer:

live_loop :flibble do
  sample :bd_haus, rate: 1
  sleep 0.5
end

Jetzt drücke den Ausführen-Button und Du wirst eine schöne Bass-Drum hören, die schnell vor sich hin hämmert. Mit einem Druck auf den Stopp-Button hört die Musik wieder auf. Aber drücke bitte noch nicht darauf … sondern probiere die folgenden Veränderungen aus:

Lass den Beat der Bass-Drum weiter laufen Verändere den Wert von sleep von 0.5 auf einen höheren Wert, etwa 1. Drücke wieder den Ausführen-Button Gemerkt? Die Geschwindigkeit der Bass-Drum hat sich verändert. Merke Dir diesen Moment. Du hast mit Sonic Pi zum ersten Mal live Code geschrieben. Wahrscheinlich wird es nicht das letzte Mal sein …

Ok, das war einfach. Verändern wir also die Zutaten ein wenig. Vor sample :bd_haus kannst Du die Zeile sample :ambi_choir, rate: 0.3 einfügen. Dein Code müsste dann so aussehen:

live_loop :flibble do
  sample :ambi_choir, rate: 0.3
  sample :bd_haus, rate: 1
  sleep 1
end

So, jetzt spiele damit herum. Verändere die Werte für rate: – was passiert, wenn Du hohe, kleine oder negative Zahlen einträgst? Hast Du gemerkt, wie sich eine sehr kleine Änderung des rate:-Wertes für das :ambi_choir-Sample auswirkt (z. B. 0.29)? Was passiert bei einem wirklich kleinen sleep-Wert? Kannst Du eine Fehlermeldung Deines Computers auslösen, wenn Du den Wert immer kleiner werden lässt? (Wenn das geschieht, nimm einfach wieder einen größeren Wert für sleep und drücke auf Ausführen).

Kommentiere eine Code-Zeile aus, indem Du ein # an ihren Anfang schreibst:

live_loop :flibble do
  sample :ambi_choir, rate: 0.3
#  sample :bd_haus, rate: 1
  sleep 1
end

Siehst Du? Jetzt ignoriert der Computer diese Zeile und wir hören den Befehl nicht mehr. Was hinter einem # steht ist ein Kommentar. Wir können dieses Zeichen benutzen, um Code schnell zu entfernen und später wieder einzufügen.

Zum Abschluss möchte ich Dir etwas zum Spielen geben. Nimm den Code unten und kopiere ihn in einen freien Puffer. Macht nix, Du musst noch nicht alles davon durchschauen, aber Du siehst, dass da zwei Loops sind – zwei Schleifen, die gleichzeitig laufen und sich endlos wiederholen. Jetzt tue, was Du am besten kannst: Experimentiere und spiele herum. Hier sind ein paar Vorschläge:

Ändere die blauen Werte hinter rate: und höre zu, wie sich das Sample verändert. Ändere die Zeitwerte von sleep und beobachte, wie die beiden Schleifen in unterschiedlicher Weise aneinander vorbei laufen. Hole die auskommentierte Sample-Zeile wieder hervor und genieße den Sound einer umgekehrt abgespielten Gitarre. Probiere, die blauen Werte von mix: auf Werte zwischen 0 (nicht im Mix) und 1 (voll im Mix) zu ändern.

Vergiss nicht, Ausführen zu drücken, dann hörst Du sofort beim nächsten Durchlauf der Loops Deine Änderungen. Wenn irgendwas schiefgeht – macht nichts! Drücke Stopp, lösche den kaputten Code und fang mit dem ursprünglichen Code noch einmal von vorn an. Denn am besten lernt man beim Fehlermachen …

live_loop :guit do
  with_fx :echo, mix: 0.3, phase: 0.25 do
    sample :guit_em9, rate: 0.5
  end
#  sample :guit_em9, rate: -0.5
  sleep 8
end
live_loop :boom do
  with_fx :reverb, room: 1 do
    sample :bd_boom, amp: 10, rate: 1
  end
  sleep 8
end

Spiele also herum und experimentiere, bis Deine Neugierde einsetzt und Du Dich fragst, wie das hier alles eigentlich wirklich funktioniert und was man noch so alles damit anstellen kann. Jetzt bist Du bereit für den Rest dieses Tutorials.

Also, worauf wartest Du …


1.2 - Die Programmoberfläche von Sonic Pi

Sonic PI hat eine sehr einfache Oberfläche zum Programmieren von Musik. Schauen wir sie uns einmal genauer an.

Sonic Pi Interface

A - Wiedergabe-Steuerung B - Editor-Steuerung C - Info und Hilfe D - Code-Editor E - Einstellungen F - Protokoll-Fenster G - Hilfe-System H - Oszilloskop I - Cue Viewer

A. Wiedergabe-Steuerung

Mit diesen pinkfarbenen Buttons kannst Du Klänge abspielen und stoppen. Der Ausführen-Button startet den Code im Editor, Stopp hält den ablaufenden Code an, Speichern schreibt den Code in eine Datei und Aufnehmen nimmt den gerade laufenden Sound (in einer WAV-Datei) auf.

B. Editor-Steuerung

Diese orangen Knöpfe erlauben es Dir, den Code-Editor zu manipulieren. Mit den Size + und Size - Knöpfen kannst du den Text vergrössern oder verkleinern.

C. Information und Hilfe

Die blauen Buttons bieten Dir den Zugriff auf Informationen, die Hilfe und die Voreinstellungen. Der Info-Button öffnet ein Fenster mit Informationen über Sonic Pi selbst und wo Du mehr Informationen und Hilfe von anderen findest. Der Help-Button zeigt/verbirgt das eingebaute Hilfesystem (F) und Prefs zeigt/verbirgt das Fenster, wo Du einige grundsätzliche Einstellungen regeln kannst.

D. Code-Editor

In diesem Bereich schreibst Du Deinen Code, komponierst Du Deine Musik und führst sie später auf. Es ist ein einfacher Texteditor, in dem Du Code eingibst und bearbeitest; so eine Art sehr einfache Textverarbeitung wie Word oder Google Docs. Der Editor färbt bestimmte Begriffe automatisch ein, je nachdem, welche Bedeutung sie innerhalb des Codes haben. Am Anfang ist das vielleicht ein wenig merkwürdig, aber Du wirst es bald nützlich finden. Zum Beispiel erkennst Du Zahlen sofort daran, dass sie blau eingefärbt sind.

E. Einstellungen

Du kannst verschiedene Einstellungen von Sonic Pi verändern, wenn Du den Prefs-Button drückst. Einige davon sind z.B.: Die Ausgabe von Mono-Ton, der Tausch der Stereokanäle, die Ausführlichkeit des Protokolls. Auf dem Raspberry Pi kannst Du hier auch noch die Lautstärke und den Ausgabekanal für den Ton auswählen.

F. Protokoll-Fenster

Wenn Dein Code abläuft, siehst Du im Protokoll-Fenster, was das Programm gerade tut. Standardmäßig siehst Du eine Nachricht für jeden erzeugten Klang und die exakte Zeit, wann der Klang ausgelöst wurde. Bei der Suche nach Fehlern kann das sehr hilfreich sein und Du verstehst schneller, was Dein Code macht.

G. Hilfe-System

Das Hilfe-System im unteren Bereich des Programmfensters ist einer der wichtigsten Teile von Sonic Pi. Du kannst es ein- und ausblenden, wenn Du den blauenHilfe-Button klickst. Es zeigt Hilfe und Informationen über alle Aspekte von Sonic Pi, einschließlich dieses Tutorials, einer Liste der mitgelieferten Synths, Samples, Beispiele, Effekte (Im Englischen wird der Begriff Effekte manchmal lautmalerisch mit FX (“ef-eks”) abgekürzt; FX bezeichnet also die Klangeffekte in Sonic Pi.) sowie eine komplette Liste aller Funktionen, die Sonic Pi zum Coden von Musik bereitstellt.

H. Oszilloskop

Das Oszilloskop kann dir die Töne die Du hörst, anzeigen. Du kannst leicht erkennen, dass die Sägezahnwelle wie eine Säge aussieht und der Beep wie eine kurvige Sinus-Kurve. Du kannst auch den Unterschied zwischen lauten und leisen Tönen anhand der Größe der Linien erkennen. Es gibt drei Felder, mit denen Du spielen kannst. Standardmäßig ist es ein kombiniertes aus dem linken und rechten Kanal, es gibt ein Stereo-Oszilloskop das für jeden Kanal einen eigenen Bereich anzeigt. Zu guter Letzt gibt es die Lissajous-Figur, sie zeigt Dir die Phasen-Beziehung zwischen dem linken und rechten Kanal. Damit kann man schöne Bilder aus Klängen malen (https://de.wikipedia.org/wiki/Lissajous-Figur).

I. Cue Viewer

All internal and external events (called cues in Sonic Pi) are automatically logged in the Cue Viewer. For example, if you have a MIDI controller connected and press one of its buttons, you’ll see a new cue event in the Cue Viewer telling you the name of the controller and which button you pressed. Once you’ve mastered the basics of making and producing sounds, you’ll start to want to cue sounds or whole sections of sounds based on events such as these. A cue event is just an indication that something happened. For example, every time a live loop spins round, it sends a cue event out which is logged in the Cue Viewer. Also, external events such as MIDI messages from connected MIDI equipment and OSC messages from other programs or computers are also displayed in the Cue Viewer. It is also possible to directly generate cue events using the cue function. Anything that appears in the Cue Viewer can be used to trigger something happening. This is covered in more detail in Sections 10 to 12 of this tutorial.


1.3 - Spielend lernen

Mit Sonic Pi kannst Du etwas über Computer und Musik zu lernen - und zwar auf eine spielerische und experimentelle Weise. Das allerwichtigste ist, dass Du Spaß hast. Und bevor Du es überhaupt bemerkt hast, wirst Du zufällig gelernt haben, Code zu schreiben, zu komponieren und Musik aufzuführen.

Es gibt keine Fehler

Einen kleinen Rat möchte ich Dir noch geben, bevor wir anfangen. In den Jahren, in denen ich Musik live programmiert habe, konnte ich eines lernen - es gibt keine Fehler, nur Gelegenheiten. Das sagen sie sonst über Jazz, aber es stimmt ebenso für Live-Coding. Es spielt keine Rolle, wie viel Erfahrung Du hast, ob Du ein absoluter Anfänger oder ein erfahrender Algoraver bist. Du wirst Code laufen lassen, der vollkommen unerwartete Ergebnisse mit sich bringt. Kann sein, dass es unglaublich cool klingt - dann mache weiter. Kann auch sein, dass es schrecklich klingt und vollkommen fehl am Platz ist. Es ist egal, wenn das passiert, wichtig ist, was Du damit als nächstes machst. Nimm den Sound, bearbeite ihn und mach daraus etwas Tolles. Die Menge wird durchdrehen.

Fang einfach an

Wenn man lernt, will man die tollen Sachen am liebsten sofort machen. Vergiss das nicht, aber sieh es als ein Ziel an, welches Du erst etwas später erreichen kannst. Denke lieber erst einmal an einfachste Sache, die Dir Spaß machen wird und die lohnend erscheint; diese Sache ist ein kleiner Schritt auf dem Weg zu dem faszinierenden Ding, das Dir im Kopf herum geht. Wenn Dir dieser einfache Schritt klar ist, versuche ihn umzusetzen, spiele damit herum und finde heraus, auf was für neue Ideen Du kommst. Du wirst bald viel Spaß haben und schnell Fortschritte machen.

Und: Sorg dafür, dass Du Deine Werke mit anderen teilst!


2 - Synths

Ok, genug der Einführung - jetzt wollen wir Klänge machen.

In diesem Abschnitt besprechen wir, wie man Synths ansteuert und manipuliert. Synth ist die Kurzform von Synthesizer - ein hochtrabendes Wort für ein Ding, das einen Klang erzeugt. Typischerweise sind Synths ziemlich kompliziert im Gebrauch, vor allem analoge Synths mit ihren vielen Kabeln und Modulen. Sonic Pi stellt Dir aber vieles davon auf eine einfache und gut zugängliche Art zur Verfügung.

Die Programmoberfläche von Sonic Pi mag simpel erscheinen, doch wenn Du willst, kannst Du damit sehr tief in ausgeklügelte Klangmanipulationen eintauchen. Also festhalten…


2.1 - Deine ersten Klänge

Sieh Dir mal den folgenden Code an:

play 70

Damit fängt alles an. Leg los, kopiere und setze das in das Code-Fenster oben im Programm ein (der große weiße Bereich unter dem Ausführen-Button). Nun klicke auf Ausführen…

Beep!

Stark. Klicke den Button nochmal. Und nochmal. Und nochmal…

Wow, verrückt, ich bin sicher, dass könntest Du den ganzen Tag lang machen. Aber halt, bevor Du Dich in einem endlosen Strom von Piepstönen verliert, versuche es mit einer anderen Zahl:

play 75

Hörst Du den Unterschied? Nimm eine kleinere Zahl:

play 60

Also, kleinere Zahlen machen tiefere Töne und größere Zahlen höhere Töne. Es ist wie bei einem Klavier, die Tasten weiter links erzeugen tiefere Töne als die Tasten weiter rechts, die höhere Töne erzeugen.

Es stellt sich heraus, dass das C in der vierten Oktave mit der Zahl 60 bezeichnet wird. play 60 spielt also ein C der vierten Oktave. Um den nächsthöheren Ton auf der Klaviatur zu spielen, musst du 1 zu 60 addieren, also play 61 schreiben. Das wird dann ein Cis (oder C# in englischer Schreibweise) sein. Um ein D, den Ton noch eins weiter, zu spielen, musst du play 62 schreiben.

Keine Sorge, wenn Du keine Ahnung hast, was das alles bedeutet. Mir ging es zu Beginn auch so. Für den Anfang reicht es zu wissen: Kleinere Zahlen bedeuten tiefere Klänge und größere Zahlen bedeuten höhere Klänge.

Akkorde

Eine Note zu spielen kann ganz lustig sein, aber mehrere zur selben Zeit zu spielen ist noch besser. Versuche es:

play 72
play 75
play 79

Wow! Wenn Du also mehrere plays hinschreibst, spielen sie alle zur selben Zeit. Versuche es selbst - welche Nummern klingen gut zusammen? Was klingt fürchterlich? Experimentiere, erforsche und erkunde selbst.

Melodie

Einzelne Noten und Akkorde zu spielen macht Spaß - aber wie wäre es mit einer Melodie? Was, wenn Du eine Note nach der anderen spielen wolltest und nicht alle zur selben Zeit? Also das geht ganz einfach, Du brauchst nur ein sleep zwischen den Noten:

play 72
sleep 1
play 75
sleep 1
play 79

Wie hübsch, ein kleines Arpeggio. Was bedeutet die 1 in sleep 1? Sie gibt die Dauer von sleep an. Tatsächlich bedeutet das: Schlafe für einen Schlag. Aber vorläufig können wir uns vorstellen, dass es bedeutet: Schlafe für eine Sekunde. Wie könnten wir unser Arpeggio schneller ablaufen lassen? Dazu brauchen wir kürzere Werte für sleep. Wie wäre es z.B. mit der Hälfte, also mit 0.5:

play 72
sleep 0.5
play 75
sleep 0.5
play 79

Achte darauf, dass die Melodie nun schneller spielt. Probiere das selbst aus, ändere die Zeiten, verwende unterschiedliche Zeiten und Noten.

Versuche einmal Zwischennoten wie play 52.3 und play 52.63. (Warum 52.2 und nicht 52,3? Im deutschsprachigen Raum schreibt man solche Zahlen normalerweise mit Komma, der Computer verwendet in seiner Programmiersprache aber die englische Schreibweise. Deshalb verwenden wir für Zahlen mit Nachkommastellen einen Punkt - also 52.3.) Es gibt überhaupt keinen Grund, nur ganze Zahlen zu verwenden. Spiel damit herum und hab Spaß dabei.

Traditionelle Notennamen

Für die unter Euch, die das Notenschreiben schon ein bisschen kennen (keine Sorge, wenn nicht - Du brauchst es nicht unbedingt) - vielleicht möchtet Ihr eine Melodie mit Notennamen anstelle von Zahlen schreiben, also C oder F#. Auch das geht mit Sonic Pi. Beachte dabei nur, dass es die englische Namensgebung für Notennamen verwendet, dort heißt Fis “F sharp” oder auch “F#” und Fes “F flat” bzw. “Fb”. Weiterhin heißt die Note H im englischen Sprachraum “B”. Du kannst folgendes machen:

play :C
sleep 0.5
play :D
sleep 0.5
play :E

Denk daran, vor den Notennamen einen Doppelpunkt : zu stellen, sodass dieser sich Pink einfärbt. Die jeweilige Oktave kannst Du als Zahl dahinter schreiben:

play :C3
sleep 0.5
play :D3
sleep 0.5
play :E4

Wenn Du eine Note um einen Halbton erhöhen willst, füge ein s hinzu, also play :Fs3. Und wenn Du die Note um einen Halbton verringern möchtest, füge ein b an, also play :Eb3.

Jetzt mach was Verrücktes und baue Deine eigenen Melodien.


2.2 - Synth-Optionen: Amp und Pan

Mit Sonic Pi kannst Du nicht nur Noten und Samples abspielen, Du kannst die Klänge mit Optionen gestalten und beeinflussen. Wir werden viele davon in diesem Tutorial behandeln, für jede Option gibt es ausführliche Informationen in der Hilfe. Zwei der nützlichsten sehen wir uns gleich mal an: Amplitude und Pan. Aber vorher lass mich kurz erklären, was Optionen eigentlich sind.

Optionen

Die Synths von Sonic Pi lassen sich über verschiedene Optionen (oder kurz: Opts) verändern. Opts sind Regler, die play oder sample übergeben werden; sie verändern und steuern die Art, wie sich die Klänge anhören. Jeder Synth hat seine eigenen Opts, um den Klang fein einzustellen. Es gibt auch Opts, die für viele Klänge gleich sind, z.B. amp: und Hüllkurven-Opts (die wir an anderer Stelle besprechen).

Opts bestehen aus zwei Teilen: Ihrem Namen (der Name des Reglers) und ihrem Wert (den Wert, auf den Du den Regler setzten möchtest). Zum Beispiel könntest Du eine Option mit dem Namen cheese: haben, der Du den Wert 1 geben möchtest.

Opts werden den Aufrufen von play und sample mit einem Komma , übergeben, dem der Name der Opt folgt, etwa amp: (vergiss den Doppelpunkt : nicht), dann eine Leerstelle und schließlich der Wert der Opt. Zum Beispiel:

play 50, cheese: 1

(Die cheese:-Opt gibt es in Wirklichkeit gar nicht, wir nehmen sie hier nur als Beispiel).

Du kannst mehrere Opts hintereinander schreiben, indem Du sie mit weiteren Kommata abtrennst:

play 50, cheese: 1, beans: 0.5

Die Reihenfolge der Opts spielt keine Rolle, so dass die folgende Zeile dasselbe tun würde wie die vorherige:

play 50, beans: 0.5, cheese: 1

Opts, die der Synth nicht kennt, lässt er einfach links liegen und ignoriert sie. (So wie cheese und beans, die ja wirklich lachhafte Namen für Optionen wären!)

Wenn Du aus Versehen zweimal dieselbe Opt mit unterschiedlichen Werten benutzt, gewinnt die letzte. Im folgenden Beispiel wird beans den Wert 2 bekommen und nicht 0.5:

play 50, beans: 0.5, cheese: 3, eggs: 0.1, beans: 2

Viele Dinge in Sonic Pi lassen sich über Optionen steuern; nimm Dir ein bisschen Zeit zu lernen, wie Du sie einsetzen kannst, und Du bist startklar. Jetzt spielen wir mal mit unserer ersten Opt: amp:.

Amplitude

Die Amplitude ist die Weise, wie sich der Computer die Lautstärke eines Klangs vorstellt. Eine hohe Amplitude ergibt einen lauteren Klang und eine niedrige Amplitude ergibt einen leiseren Klang. So wie Sonic Pi Zahlen dazu benutzt, um Zeit und Töne darzustellen, bildet es auch die Lautstärke mit Zahlen ab. Eine Amplitude von 0 bedeutet Stille (Du wirst nichts hören), eine Amplitude von 1 steht für normale Lautstärke. Du kannst die Amplitude aufdrehen auf 2, 10, 100. Aber Vorsicht: Wenn die Amplitude aller gemeinsamen Klänge zu hoch wird, setzt Sonic Pi einen sogenannten Kompressor ein, damit die Klänge nicht zu laut für Dein Ohr werden. Oft klingt das dann matschig und schräg. Verwende also lieber niedrigere Amplituden, das heißt zwischen 0 und 0.5, um die Kompression zu verhindern.

Amp verwenden

Um die Amplitude eines Klangs zu ändern, setze die Opt amp: ein. Mit 0.5 lässt Du zum Beispiel den Klang mit halber Amplitude spielen:

play 60, amp: 0.5

Und für die doppelte Amplitude:

play 60, amp: 2

Die amp:-Opt beeinflusst nur den Aufruf von play, mit der sie unmittelbar zusammenhängt. Das heißt, in dem folgenden Beispiel wird der erste Aufruf von play mit halber Lautstärke gespielt und der zweite wieder mit der Standardlautstärke (1):

play 60, amp: 0.5
sleep 0.5
play 65

Natürlich kannst Du für jeden Aufruf von play andere Werte für amp: festlegen:

play 50, amp: 0.1
sleep 0.25
play 55, amp: 0.2
sleep 0.25
play 57, amp: 0.4
sleep 0.25
play 62, amp: 1

Panning

Eine weitere interessante Opt ist pan:. Ihr Wert gibt an, aus welcher Richtung der Klang kommt, wenn wir in Stereo hören. Der Wert -1 schiebt den Klang ganz nach links, 0 steht für die Mitte und 1 lässt den Klang nur aus dem rechten Lautsprecher kommen. Natürlich kannst Du jeden Wert zwischen -1 und 1 verwenden, um Deine Klänge im Stereofeld zu positionieren.

Spiel einen Klang, der nur aus linken Lautsprecher kommt:

play 60, pan: -1

Jetzt aus dem rechten Lautsprecher:

play 60, pan: 1

Nun lass den Klang aus der Mitte herauskommen (die Standardposition):

play 60, pan: 0

Jetzt leg’ los und verändere die Amplitude und das Panning Deiner Sounds!


2.3 - Synths wechseln

Bisher hatten wir viel Spaß damit Pieptöne zu erzeugen. Aber wahrscheinlich langweilt es dich langsam, immer wieder denselben Klang zu hören. Ist das alles, was Sonic Pi zu bieten hat? Live-Coding kann doch sicher mehr als nur einen einfachen Piepton zu spielen? Aber klar doch! In diesem Abschnitt schauen wir uns die aufregende Vielfalt der Klänge, die uns Sonic Pi bietet, an.

Synths

Sonic Pi bringt eine Palette an Instrumenten mit, die Synths heißen, eine Kurzform für Synthesizer (Klangerzeuger). Samples sind vorher aufgenommene Klänge; demgegenüber erzeugen Synths neue Klänge, je nachdem, wie Du sie anlegst (wie das geht, werden wir später sehen). Die Synths von Sonic Pi sind sehr wirkungsvoll und ausdrucksstark und man kann damit tolle Sachen machen. Als erstes lernen wir, wie man einen Synth auswählt, den man benutzen möchte.

Brilliante Sägen und Prophets

Einen spaßigen Sound ergibt die Sägezahn-Welle - probieren wir es mal aus:

use_synth :saw
play 38
sleep 0.25
play 50
sleep 0.25
play 62
sleep 0.25

Nehmen wir einen anderen Klang - den Prophet:

use_synth :prophet
play 38
sleep 0.25
play 50
sleep 0.25
play 62
sleep 0.25

Wie wäre es damit, beide Sounds zu verbinden? Zuerst nacheinander:

use_synth :saw
play 38
sleep 0.25
play 50
sleep 0.25
use_synth :prophet
play 57
sleep 0.25

Jetzt machen wir mehrere Töne gleichzeitig, indem wir keine Pausen mit sleep zwischen Aufrufen von play einfügen:

use_synth :tb303
play 38
sleep 0.25
use_synth :dsaw
play 50
sleep 0.25
use_synth :prophet
play 57
sleep 0.25

Hast Du bemerkt, dass das use_synth-Kommando nur die nachfolgenden play-Kommandos beeinflusst? Stell Dir das als einen großen Schalter vor - neue Aufrufe von play werden immer den Synth benutzen, auf den der Schalter gerade zeigt. Du kannst diesen Schalter mit use_synth auf einen anderen Synth umschalten.

Synths entdecken

Welche Synths Sonic Pi für Dich bereitstellt, kannst Du im Synths-Bereich auf der linken Seite des Hilfe-Fensters entdecken (gleich neben Fx). Es gibt über 20 Stück. Hier sind einige meiner Lieblings-Synths:

:prophet :dsaw :fm :tb303 :pulse

Spiel mal ein bisschen herum, indem Du in Deinem Stück die Synths wechselst. Kombiniere unterschiedliche Synths oder setze sie für unterschiedliche Stellen in Deinem Stück ein.


2.4 - Dauer und Hüllkurven

Wie wir schon vorher gesehen haben, können wir mit dem sleep-Kommando steuern, wann ein Klang zu spielen anfängt. Bislang konnten wir aber noch nicht die Dauer eines Klangs steuern.

Um die Dauer von Klängen auf einfache, aber tiefgehende Weise zu beeinflussen, bietet Sonic Pi das Prinzip der ADSR-Hüllkurve. (Der englische Begriff für Hüllkurve heißt Envelope, wir werden später sehen, was ADSR genau bedeutet). Eine Hüllkurve hat zwei praktische Eigenschaften:

Kontrolle über die Dauer eines Klangs Kontrolle über die Amplitude (Lautstärke) eines Klangs

Dauer

Die Dauer bestimmt, wie lange ein Klang klingt. Eine längere Dauer bedeutet, dass Du den Klang länger hören kannst. Alle Klänge in Sonic Pi haben eine Hüllkurve, die beeinflusst werden kann, und die gesamte Dauer dieser Hüllkurve bestimmt die Dauer des Klangs. Deshalb kann man über die Kontrolle der Hüllkurve die Dauer des Klangs beeinflussen.

Amplitude

Die ADSR-Hüllkurve kontrolliert nicht nur die Dauer, sondern ermöglicht Dir auch die genaue Kontrolle über die Amplitude eines Klangs. Alle Klänge beginnen und enden mit Stille; dazwischen ist irgendetwas hörbar. Hüllkurven erlauben Dir, die Amplitude der hörbaren Anteile des Klanges zu verschieben, zu verlängern und zu verkürzen. Es ist so, als würdest Du jemanden sagen, wie er die Lautstärke einer Musik lauter und leiser drehen soll. Du könntest denjenigen z.B. bitten: “Fang bitte mit einer Stille an, dreh dann die Lautstärke langsam bis zum Anschlag hoch, lass es eine Weile so und blende dann schnell runter, bis man nichts mehr hört”. Genau dafür sind in Sonic Pi die Hüllkurven da.

Wie wir vorher schon gesehen haben, bedeutet eine Amplitude von 0 Stille und eine Amplitude von 1 entspricht der normalen Lautstärke.

Jetzt wollen wir uns alle Teile einer Hüllkurve einmal ansehen.

Release-Zeit

Wenn man Sonic Pi nichts anderes sagt, verwendet jeder Synth eine sehr einfache Hüllkurve mit einer Release-Zeit von 1. Solange dauert es, bis der Synth ausklingt - eine Schlagzeit (was beim BPM-Wert 60 genau 1 Sekunde entspricht):

play 70

Diesen Ton wirst Du 1 Sekunde lang hören. Los, stopp die Zeit. :-) Es bedeutet das gleiche für diese etwas längere, ausführliche Variante:

play 70, release: 1

Du hast gemerkt, dass dieser Ton genau gleich klang (der Klang war eine Sekunden lang zu hören). Aber jetzt kannst Du sehr einfach die Dauer über die release:-Opt verändern:

play 60, release: 2

Mit einer sehr kurzen Release-Zeit wird der Synth-Klang sehr kurz:

play 60, release: 0.2

Die Dauer des Ausklingens eines Klangs nennt sich Release-Phase. Normalerweise ist das ein lineares Ausklingen (also eine gerade Linie), wie das folgende Diagramm zeigt:

release envelope

Die senkrechte Linie ganz links im Bild zeigt, dass der Klang mit einer Amplitude von 0 startet, jedoch sofort auf die volle Höhe geht (das ist die Attack-Phase, die wir gleich ansehen werden). Wenn die volle Höhe erreicht ist, geht die Amplitude in einer geraden Linie bis auf Null zurück, wobei dies so lange dauert, wie es mit release festgelegt wurde. Je länger die Release-Zeit eines Synth, desto länger klingt dieser aus.

Deshalb kannst Du mit der Release-Zeit die Länge eines Klangs ändern. Spiele einmal mit unterschiedlichen Release-Zeiten herum.

Attack-Zeit

Normalerweise ist die Attack-Phase für alle Synths 0, die Amplitude geht also sofort von 0 auf 1 hoch. Dies gibt dem Synth einen perkussiven Klang. Vielleicht möchtest Du den Klang jedoch einblenden. Dies kannst Du mit der attack-Opt erreichen. Blende einige Klänge ein:

play 60, attack: 2
sleep 3
play 65, attack: 0.5

Du kannst mehrere Opts zur selben Zeit anwenden. Zum Beispiel für eine kurze Attack- und eine lange Release-Zeit:

play 60, attack: 0.7, release: 4

Diese Hüllkurve mit einem kurzen Attack und langem Release sieht so aus:

attack release envelope

Oder mal andersherum: Versuche einen langen Attack und einen kurzen Release:

play 60, attack: 4, release: 0.7

long attack short release envelope

Schließlich kannst Du auch einen kurzen Attack und Release für kürzere Klänge verwenden.

play 60, attack: 0.5, release: 0.5

short attack short release envelope

Sustain-Zeit

Zusätzlich zu den Attack- und Release-Zeiten kannst Du auch die Sustain-Zeit bestimmen. Das ist die Zeitdauer, die der Klang anhält, wenn er die eingestellte Lautstärke erreicht hat, also zwischen dem Attack- und dem Release-Stadium.

play 60, attack: 0.3, sustain: 1, release: 1

ASR envelope

Mit der Sustain-Zeit kannst Du wichtige Klänge in einem Mix hervorheben, bevor möglicherweise der Release einsetzt. Natürlich kann man ohne weiteres sowohl den attack: als auch den release: auf 0 setzen und nur den Sustain gebrauchen. Damit bekommt man einen Klang ohne Ein- und Ausblendung. Aber Vorsicht, ein Release von 0 kann Klickgeräusche in der Aufnahme verursachen. Meist ist ein geringer Wert wie z.B. 0.2 besser.

Decay-Zeit

Und falls Du alles ganz genau beeinflussen möchtest, kannst Du noch die Decay-Zeit festlegen. Das ist die Phase der Hüllkurve, die zwischen die Attack- und die Release-Phase passt; sie gibt die Zeit an, wo die Amplitude hinunterfällt vom attack_level: zum decay_level: (welche den gleichen Wert wie sustain_level: erhält, wenn Du es nicht anders vorgibst). Normalerweise steht die Opt decay: auf 0, während sowohl das Attack- als auch das Sustain-Level auf 1 stehen. Damit die Decay-Zeit einen hörbaren Effekt hat, müssen Attack und Sustain also auch angegeben werden:

play 60, attack: 0.1, attack_level: 1, decay: 0.2, sustain_level: 0.4, sustain: 1, release: 0.5

ADSR envelope

Decay-Level

Normalerweise hat die Option decay_level: den selben Wert wie sustain_level:, aber Du kannst sie auch bewusst einen anderen Wert setzen. So hast Du volle Kontrolle über die Hüllkurve und kannst zum Beispiel eine wie diese vorgeben:

play 60, attack: 0.1, attack_level: 1, decay: 0.2, decay_level: 0.3, sustain: 1, sustain_level: 0.4, release: 0.5

ASR envelope

Ebenso ist für decay_level: ein höherer Wert als sustain_level: möglich:

play 60, attack: 0.1, attack_level: 0.1, decay: 0.2, decay_level: 1, sustain: 0.5, sustain_level: 0.8, release: 1.5

ASR envelope

ADSR-Hüllkurven

Fassen wir noch einmal zusammen: Die ADSR-Hüllkurven von Sonic Pi bestehen aus den folgenden Phasen:

Attack - die Zeit, welche die Amplitude von 0 bis zum attack_level benötigt, Decay - die Zeit, in der die Amplitude vom attack_level zum sustain_level übergeht, Sustain - die Zeit, in der sich die Amplitude auf dem sustain_level hält, Release - die Zeit, welche die Amplitude vom sustain_level auf 0 bringt

Eins ist wichtig: der Klang dauert so lange wie die Summe aller Zeiten der unterschiedlichen Phasen. Deshalb hat der folgende Klang eine Dauer von 0.5 + 1 + 2 + 0.5 = 4 Schlägen:

play 60, attack: 0.5, attack_level: 1, decay: 1, sustain_level: 0.4, sustain: 2, release: 0.5

Jetzt weißt Du, wie Du Deine Klänge mit Hüllkurven verändern kannst…


3 - Samples

Du kannst auch aufgenommene Sounds in Deiner Musik verwenden. In der großen Tradition des Hip-Hop nennen wir diese aufgenommenen Sounds auch Samples. Wenn Du ein Mikrophon mit nach draußen nimmst und den zarten Klang der Regentropfen auf einem Zeltdach aufnimmst, dann ist das Ergebnis ein Sample.

Mit Samples kann man in Sonic Pi viele tolle Sachen machen. Es sind über 130 freie Samples zum sofortigen Gebrauch dabei und Du kannst auch Deine eigenen Samples einbauen und verändern. Legen wir los…


3.1 - Samples ansteuern

Pieptöne zu spielen ist erst der Anfang. Mit aufgenommenen Samples hat man noch viel mehr Spaß. Probier mal:

sample :ambi_lunar_land

Sonic Pi enthält viele Samples, mit denen Du spielen kannst. Du kannst sie ebenso wie das play-Kommando benutzen. Um mehrere Samples gleichzeitig zu spielen, schreib sie einfach untereinander:

play 36
play 48
sample :ambi_lunar_land
sample :ambi_drone

Wenn Du sie nacheinander spielen willst, füge einen sleep-Befehl ein:

sample :ambi_lunar_land
sleep 1
play 48
sleep 0.5
play 36
sample :ambi_drone
sleep 1
play 36

Hast Du bemerkt, dass Sonic Pi nicht wartet, bis ein Klang beendet ist, bevor der nächste beginnt? Der sleep-Befehl beschreibt nur, wann ein Klang ausgelöst wird, und wann der nächste nachfolgt. Damit kannst Du Klänge in Schichten übereinander legen und interessante überlappende Effekte herstellen. Später sehen wir uns an, wie man die Länge von Klängen mit Hüllkurven kontrollieren kann.

Samples entdecken

Du kannst die Samples, die Sonic Pi mitbringt, auf zwei Arten entdecken. Erstens kannst Du dieses Hilfesystem benutzen. Klicke auf Samples im Menü auf der linken Seite, wähle eine Gruppe aus und Du findest eine Liste aller verfügbaren Klänge.

Zweitens hilft Dir das Auto-Completion System. Tipp einfach den Anfang eines Gruppennamens von Samples ein, also: sample :ambi_. Nun klappt ein Menü mit allen dazu passenden Samples auf. Versuche einen der folgenden Anfänge von Gruppennamen:

:ambi_ :bass_ :elec_ :perc_ :guit_ :drum_ :misc_ :bd_

Jetzt leg los und mische Samples in Deine Komposition!


3.2 - Sample-Parameter: Amp und Pan

Genauso wie mit den Synths können wir auch unsere Sounds mit Parametern steuern. Der selbe Parameter-Mechanismus funktioniert auch mit Samples. Besuchen wir unsere Freunde amp: and pan: noch einmal.

Die Amplitude von Samples verändern

Du kannst die Amplitude (Lautstärke) von Samples so steuern, wie Du es schon von den Synths kennst:

sample :ambi_lunar_land, amp: 0.5

Das Stereo-Feld von Samples verändern

Wir können auch den pan:-Parameter auf Samples anwenden. Hier zum Beispiel spielen wir den Amen-Break zunächst für das linke und nach der Hälfte für das rechte Ohr:

sample :loop_amen, pan: -1
sleep 0.877
sample :loop_amen, pan: 1

0.877 ist genau die Hälfte der Dauer des Amen-Breaks in Sekunden.

Beachte: Samples berücksichtigen nicht die Grundeinstellungen für einen Synth, die Du mit use_synth_defaults setzen kannst (aber dazu kommen wir später noch).


3.3 - Samples dehnen

Jetzt können wir schon eine ganze Menge Synths und Samples spielen und damit Musik machen. Es wird Zeit zu lernen, wie wir diese Synths und Samples verändern können. Das macht unsere Musik einzigartiger und spannender. Als erstes sehen wir uns an, wie wir Samples strecken und stauchen.

Wie Samples Klänge darstellen

Samples sind aufgenommene Klänge und werden als eine Zahlenreihe von Messwerten (deshalb “Sample”) gespeichert. Diese Zahlen sagen der Lautsprechermembran, wie sie sich bewegen muss, um den Klang wiederzugeben. Die Lautsprechermembran kann sich nach innen und nach außen bewegen und die Zahlen geben deshalb an, wie weit sich die Membran zu jedem Zeitpunkt nach innen oder außen bewegen soll. Um einen Klang als Aufnahme wirklichkeitsgetreu wiederzugeben, muss das Sample je Sekunde viele tausend Zahlen speichern. Sonic Pi nimmt diese Zahlenreihe und gibt sie in der richtigen Samplerate (bzw. Samplingrate oder Abtastrate) an den Lautsprecher in Deinem Computer, so dass der Klang richtig wiedergegeben wird. Es macht aber auch Spaß, die Samplerate zu beeinflussen, denn das verändert den Klang.

Die Samplerate ändern

Lass uns mit einem der Ambient-Klänge spielen: ambi_choir. Beim Befehl sample kannst Du mit der Opt rate die Samplerate verändern:

sample :ambi_choir, rate: 1

Das Sample wird unverändert mit der normalen Samplerate (1) abgespielt, also nichts Besonderes. Aber wir können die Zahl jederzeit verändern. Was passiert bei 0.5?

sample :ambi_choir, rate: 0.5

Wow! Was ist denn jetzt los? Also, hier passieren zwei Dinge. Erstens braucht das Sample doppelt so lange und zweitens klingt er eine Oktave niedriger. Sehen wir uns das mal genauer an.

Lasst uns stretchen

Mit dem Amen-Break-Sample macht das Strecken und Zusammenstauchen besonders viel Spass. Mit der normalen Samplerate passt das Sample gut in einen Drum ‘n’ Bass-Track:

sample :loop_amen

Mit einer anderen Samplerate passt es zu einer ganz anderen Stilrichtung. Versuche es mal mit halber Samplerate für Hip-Hop der alten Schule:

sample :loop_amen, rate: 0.5

Wenn wir es beschleunigen, betreten wir das Jungle-Territorium:

sample :loop_amen, rate: 1.5

Und als letzten Trick - sehen wir mal, was passiert, wenn wir eine negative Rate angeben:

sample :loop_amen, rate: -1

Boah! Das Sample spielt rückwärts! Jetzt probiere mit vielen unterschiedlichen Samples und unterschiedlichen Sampleraten herum. Versuch es mit sehr hohen oder mit verrückt langsamen. Finde heraus, welche spannenden Klänge Du damit produzieren kannst.

Eine einfache Erklärung der Samplerate

Man kann sich Samples gut als Sprungfedern vorstellen. Wenn man die Samplerate verändert, ist das so, als ob man die Feder zusammendrückt oder auseinanderzieht. Wenn Du ein Sample mit der doppelten Samplerate abspielst, dann drückst Du die Feder zusammen, bis sie nur noch die Hälfte ihrer normalen Länge hat. Das Sample braucht also auch nur die Hälfte der Abspielzeit. Wenn Du das Sample mit halber Samplerate spielst, dann ziehst Du die Feder auf ihre doppelte Länge auseinander. Das Sample dauert also nun doppelt so lange. Je mehr Du zusammendrückst (höhere Rate), desto kürzer das Sample, je mehr Du auseinanderziehst (geringere Rate), desto länger dauert das Sample.

Wenn man eine Feder zusammendrückt, dann erhöht man ihre Dichtheit (die Anzahl der Windungen je Zentimeter) - das ist so ähnlich wie bei einem Sample, der höher klingt. Wenn man die Feder auseinanderzieht, dann verringert sich ihre Dichtheit, vergleichbar einem Klang, der tiefer klingt.

Die Mathematik hinter der Samplerate

(Dieser Abschnitt ist für diejenigen gedacht, die noch etwas mehr in die Tiefe gehen möchten. Er kann gerne übersprungen werden …)

Wie wir oben gesehen haben, wird ein Sample durch eine lange Liste von Zahlen dargestellt, die der Lautsprechermembran sagen, wo sie zu welchem Zeitpunkt sein soll. Wir können diese Zahlenliste nehmen und eine Kurve zeichnen, die ungefähr so aussieht:

sample graph

Vielleicht hast Du Bilder wie dieses schon einmal gesehen. Das ist die Kurvenform eines Sample. Es ist bloß eine Kurve aus Zahlenwerten. Typischerweise entsteht eine solche Kurve aus 44100 Datenpunkten je Sekunde (das hat mit dem Nyquist-Shannon-Abtasttheorem zu tun). Wenn also das Sample 2 Sekunden dauert, dann wird die Kurve aus 88200 Zahlen gebildet, die wir an den Lautsprecher mit einer Datenrate von 44100 Datenpunkten pro Sekunde senden. Natürlich könnten wir es mit der doppelten Datenrate senden, also 88200 Datenpunkten pro Sekunde. Dann würde das Sample nur eine Sekunde lang dauern. Wir können es auch mit der halben Datenrate abspielen; das wären dann 22050 Datenpunkte pro Sekunde und würde 4 Sekunden dauern.

Die Sample-Dauer ist also abhängig von der Datenrate:

Wird die Abtastrate verdoppelt, halbiert das die Abspielzeit, Wird die Abtastrate halbiert, dann verdoppelt sich die Abspielzeit, Verwendest Du eine Abtastrate von einem Viertel, dann vervierfachst Du die Abspielzeit. Verwendest Du eine Abtastrate von 1/10, dann dauert die Abspielzeit 10mal länger.

Es gibt eine Formel dafür:

neue_sample_dauer = (1 / rate) * sample_dauer 

Eine Veränderung der Samplerate beeinflusst auch die Tonhöhen des Samples. Die Frequenz oder Tonhöhe einer Kurve wird dadurch bestimmt, wie schnell sie hoch und runter geht. Unsere Gehirne machen aus schnellen Bewegungen einer Lautsprechermembran hohe Töne und aus langsamen tiefe. Deshalb kannst Du manchmal sogar sehen, wie sich ein großer Basslautsprecher bewegt, wenn ein sehr tiefer Ton rauskommt. Er bewegt sich dann wesentlich langsamer als ein Lautsprecher, der hohe Töne wiedergibt.

Wenn Du eine Kurve nimmst und sie zusammendrückst, wird sie je Sekunde öfter hoch und runter gehen als vorher. Der Ton wird dann auch höher sein. Wenn man die Auf- und Abbewegungen je Sekunde verdoppelt (Oszillationen), dann wird die Frequenz, die Tonhöhe, verdoppelt. Also, wenn Du Dein Sample mit doppelter Samplerate abspielst, wird es auch doppelt so hoch klingen; andererseits: eine Halbierung der Samplerate wird auch die Frequenz halbieren. Andere Sampleraten werden dementsprechend die Tonhöhe beinflussen.


3.4 - Samples mit Hüllkurven

Es ist auch möglich, die Dauer und Amplitude eines Sample mit einer ADSR-Hüllkurve zu verändern. Das funktioniert jedoch ein wenig anders als die ADSR-Hüllkurven bei den Synths. Mit Sample-Hüllkurven kannst Du die Amplitude und die Dauer eines Samples nur verringern - niemals vergrößern. Das Sample wird entweder stoppen, wenn seine normale Laufzeit vorbei ist, oder wenn die Hüllkurve das Sample begrenzt - je nachdem welche kürzer ist. Mit einem sehr langen release: kannst Du ein Sample also nicht über seine normale Laufzeit hinaus verlängern.

Amen-Hüllkurven

Kommen wir zu unserem alten Freund zurück, dem Amen-Break:

sample :loop_amen

Ohne Opts hören wir das Sample in seiner gesamten Länge und mit voller Lautstärke. Mit der attack:-Opt können wir das Sample einblenden lassen:

sample :loop_amen, attack: 1

Wähle für ein kürzeres Einblenden einen kürzeren Attack-Wert:

sample :loop_amen, attack: 0.3

Auto-Sustain

Beim Sustain-Wert unterscheidet sich das Verhalten einer ADSR-Hüllkurve. Bei der Hüllkurve für Standard-Synths steht der Sustain normalerweise auf 0 - außer Du setzt den Wert ausdrücklich. Bei Samples wird der Sustain-Wert auf einen automagical Wert gesetzt - nämlich die Zeit, die es braucht, bis das gesamte Sample abgelaufen ist. Darum hören wir das Sample komplett, wenn wir keine Default-Werte übergeben. Wenn die Werte für Attack, Decay, Sustain und Release alle 0 wären, würden wir keinen Pieps hören. Deshalb berechnet Sonic Pi zunächst, wie lange das Sample von sich aus dauert, zieht etwaige Dauern für Attack, Decay und Release davon ab und setzt die restliche Zeit als Sustain-Wert. Wenn die Werte von Attack, Decay und Release zusammengenommen länger dauern als das gesamte Sample, wird der Sustain-Wert einfach auf 0 gesetzt.

Ausblenden

Um das auszuprobieren, schauen wir uns den Amen-Break genauer an. Wir fragen Sonic Pi, wie lang das Sample ist:

print sample_duration :loop_amen

Es wird 1.753310657596372 ausgeben; das ist die Länge des Sample in Sekunden. Wir runden das einmal bequemerweise auf 1.75 ab. Wenn wir nun den Release-Wert auf 0.75 setzen, wird etwas erstaunliches passieren:

sample :loop_amen, release: 0.75

Die erste Sekunde des Sample wird mit voller Lautstärke gespielt, danach wird über eine Periode von 0.75 Sekunden ausgeblendet. Das ist der Auto-Sustain in Aktion. Standardmäßig berechnet Sonic Pi den Release immer vom Ende des Samples aus. Wenn unser Sample 10.75 Sekunden lang wäre, würden die ersten 10 Sekunden in voller Lautstärke gespielt bevor dann eine Ausblende über 0.75 Sekunden erfolgt.

Zur Erinnerung: Normalerweise blendet release: das Ende des Sample aus.

Ein- und Ausblenden

Wir können attack: und release: zusammen mit Auto-Sustain nutzen, um über die Laufzeit des Samples ein- und auszublenden:

sample :loop_amen, attack: 0.75, release: 0.75

Da die Gesamtdauer des Sample 1.75s beträgt und unsere Attack- und Release-Phasen zusammen 1.5s ergeben, erhält der Sustain automatisch die Länge 0.25s. So ist es ganz einfach, das Sample ein- und auszublenden.

Ausdrücklich angegebener Sustain

Wir können ohne weiteres das normales Synth-ADSR-Verhalten aktivieren, indem wir sustain: auf den Wert 0 setzen:

sample :loop_amen, sustain: 0, release: 0.75

Jetzt spielt unser Sample insgesamt nur 0.75 Sekunden. Mit dem Standardwert 0 für attack: und decay: springt das Sample direkt auf die volle Lautstärke, bleibt dort für 0s und fällt dann innerhalb der Release-Phase mit einer Dauer von 0.75s auf die Lautstärke 0 ab.

Perkussive Becken

Wir können dieses Verhalten gut dazu benutzen, um länger klingende Samples in kürzere, perkussivere Versionen zu verwandeln. Sieh Dir das Sample :drum_cymbal_open an:

sample :drum_cymbal_open

Man kann hören, wie das Becken eine Zeit lang ausklingt. Mit einer leicht veränderten Hüllkurve klingt es perkussiver:

sample :drum_cymbal_open, attack: 0.01, sustain: 0, release: 0.1

Mit einer längeren Sustain-Dauer kannst Du es so klingen lassen, als ob das Becken erst angeschlagen und dann abgedämpft würde:

sample :drum_cymbal_open, attack: 0.01, sustain: 0.3, release: 0.1

Jetzt versuch einmal, Hüllkurven über Samples zu legen. Verändere auch die Samplerate; damit kannst Du sehr interessante Ergebnisse erzielen.


3.5 - Teil-Samples

Dieses Kapitel schließt unsere Erkundung von Sonic Pi’s Sample-Player ab. Fassen wir noch einmal zusammen. Wir haben uns angesehen, wie wir Samples abspielen können:

sample :loop_amen

Dann haben wir gesehen, dass wir die Samplerate ändern können - etwa um ein Sample mit halber Geschwindigkeit abzuspielen:

sample :loop_amen, rate: 0.5

Als nächstes haben wir einen Blick darauf geworfen, wie wir ein Sample ein- und ausblenden lassen (hier zum Beispiel mit halber Geschwindigkeit):

sample :loop_amen, rate: 0.5, attack: 1

Wir haben uns auch angeschaut, wie wir ein Sample am Anfang perkussiver klingen lassen können, indem wir sustain: ausdrücklich einen Wert zuweisen, und sowohl der Attack als auch der Release kurze Werte bekommen:

sample :loop_amen, rate: 2, attack: 0.01, sustain: 0, release: 0.35

Wäre es aber nicht toll, wenn wir ein Sample nicht immer vom Anfang starten lassen müssten? Wäre es nicht auch prima, wenn das Ende des klingenden Sample nicht immer seinem wirklichen Ende entsprechen müsste?

Einen Startpunkt bestimmen

Man kann auch einen beliebigen Startpunkt eines Sample als Wert zwischen 0 und 1 auswählen. Dabei ist 0 der Anfang, 1 das Ende und 0.5 die Mitte des Samples. Spielen wir also jetzt die zweite Hälfte des Amen Break:

sample :loop_amen, start: 0.5

Oder vielleicht das letzte Viertel des Sample:

sample :loop_amen, start: 0.75

Einen Endpunkt bestimmen

Ebenso können wir mit einem Wert zwischen 0 und 1 einen beliebigen Endpunkt im Sample festlegen. Beenden wir den Amen-Break nach der ersten Hälfte:

sample :loop_amen, finish: 0.5

Start- und Endpunkt bestimmen

Natürlich können das auch kombinieren und so einen beliebigen Abschnitt des Sample abspielen. Wie wäre es mit einem kurzen Abschnitt aus der Mitte:

sample :loop_amen, start: 0.4, finish: 0.6

Was passiert, wenn wir eine Startposition nach der Endposition auswählen?

sample :loop_amen, start: 0.6, finish: 0.4

Cool! Dann spielt es rückwärts!

In Kombination mit der Samplerate

Diese neue Fähigkeit, beliebige Abschnitte eines Klangs zu spielen, lässt sich mit unserem Freund rate: kombinieren. Zum Beispiel können wir einen sehr kurzen Abschnitt des Amen-Breaks sehr langsam spielen:

sample :loop_amen, start: 0.5, finish: 0.7, rate: 0.2

In Kombination mit Hüllkurven

Wenn wir all dies mit unseren ADSR-Hüllkurven kombinieren, kriegen wir interessante Ergebnisse:

sample :loop_amen, start: 0.5, finish: 0.8, rate: -0.2, attack: 0.3, release: 1

Jetzt leg los und hab Spaß, Samples mit diesem Zeug aufzumischen…


3.6 - Samples aus externen Quellen

Mit den mitgelieferten Samples kannst Du schnell einsteigen, aber vielleicht möchtest Du mit anderen aufgenommenen Klängen in Deiner Musik experimentieren. Sonic Pi unterstützt das ausdrücklich. Lass uns aber zunächst über die Übertragbarkeit Deines Stücks sprechen.

Übertragbarkeit

Wenn Du Dein Stück nur mit Hilfe der eingebauten Synths und Samples komponierst, braucht man nur den Code, um Deine Musik so abzuspielen, wie Du sie geschrieben hast. Denk darüber einen Augenblick nach - das ist erstaunlich! Ein bisschen Text, den Du per Email verschicken oder auf Gist ablegen kannst, reicht vollkommen aus, um Deine Klänge wieder abzuspielen. So wird es sehr einfach, Deine Kompositionen mit Deinen Freunden zu teilen. Sie brauchen nur ein wenig Code von Dir.

Wenn Du nun selbst aufgenommene Samples verwendest, verlierst Du diese Übertragbarkeit. Denn jetzt braucht man zum Abspielen auf einem anderen Rechner nicht nur den Code, sondern auch Deine Samples.

Lokale Samples

Wie geht das nun, eine beliebige WAV-, AIFF- oder FLAC-Datei auf Deinem Computer zu spielen? Dafür musst Du sample nur den Pfad der Datei übergeben:

# Raspberry Pi, Mac, Linux
sample "/Users/sam/Desktop/my-sound.wav"
# Windows
sample "C:/Users/sam/Desktop/my-sound.wav"

Sonic Pi wird das Sample nun automatisch laden und spielen. Alle anderen Standard-Parameter, die Du schon kennengelernt hast, kannst Du jetzt auch für Dein eigenes Sample einsetzen:

# Raspberry Pi, Mac, Linux
sample "/Users/sam/Desktop/my-sound.wav", rate: 0.5, amp: 0.3
# Windows
sample "C:/Users/sam/Desktop/my-sound.wav", rate: 0.5, amp: 0.3

3.7 - Sample-Pakete

Hinweis: Dieser Abschnitt des Tutorials behandelt den fortgeschrittenen Umgang mit großen Verzeichnissen Deiner eigenen Samples. Das wird nützlich, wenn Du eigene Sample Packs heruntergeladen oder gekauft hast und diese in Sonic Pi nutzen möchtest.

Du kannst diesen Abschnitt überspringen, wenn Dir die mitgelieferten Samples ausreichen und Du keine eigenen brauchst.

Wenn du mit großen Ordnern externer Samples arbeitest, kann es mühselig sein, jedes Mal den vollständigen Pfad eingeben zu müssen, um ein bestimmtes Sample abzuspielen.

Sagen wir, Du hast zum Beispiel folgenden Ordner auf Deinem Computer:

/pfad/zu/meinen/samples/

Wenn wir in den Ordner schauen, finden wir folgende Samples:

100_A#_melody1.wav 100_A#_melody2.wav 100_A#_melody3.wav 120_A#_melody4.wav 120_Bb_guit1.wav 120_Bb_piano1.wav

Üblicherweise können wir den vollständigen Pfad nutzen um das Piano-Sample abzuspielen:

sample "/pfad/zu/meinen/samples/120_Bb_piano1.wav"

Wenn wir das Gitarren-Sample abspielen wollen können wir ebenfalls den vollständigen Pfad nutzen:

sample "/pfad/zu/meinen/samples/120_Bb_guit.wav"

Wie auch immer, beide Aufrufe verlangen von uns, dass wir die Namen der Samples innerhalb des Verzeichnisses kennen. Was, wenn wir nur schnell die einzelnen Samples nacheinander hören wollen?

Sample-Packs indizieren

Wenn wir das erste Sample in einem Verzeichnis abspielen wollen, müssen wir nur den Verzeichnisnamen zum Sample und den Index 0 eingeben. Das geht so:

sample "/pfad/zu/meinen/samples/", 0

Wir können sogar eine Vernküpfung zu unserem Verzeichnis anlegen, indem wir eine Variable nutzen:

samps = "/pfad/zu/meinen/samples/"
sample samps, 0

Wenn wir nun das zweite Sample in unserem Verzeichnis abspielen wollen, müssen wir lediglich den Index 1 anfügen:

samps = "/pfad/zu/meinen/samples/"
sample samps, 1

Beachte, dass wir nicht mehr die Namen der Samples im Verzeichnis kennen müssen, wir benötigen nur das Verzeichnis (oder haben eine Verknüpfung zu diesem). Wenn wir nach einem Index fragen, der größer als die Anzahl der Samples ist, fängt die Liste einfach von vorne an - wie bei Ringen. Also egal welche Nummer wir nutzen, es ist garantiert, dass wir ein Sample aus dem Verzeichnis bekommen.

Sample-Packs filtern

Gewöhnlich reicht das Indizieren aus, aber manchmal benötigen wir mehr Möglichkeiten beim Organisieren unserer Samples. Glücklicherweise ergänzen viele Sample-Packs nützliche Informationen in ihre Dateinamen. Lass uns noch einmal einen Blick auf die Dateinamen in unserem Verzeichnis werfen:

100_A#_melody1.wav 100_A#_melody2.wav 100_A#_melody3.wav 120_A#_melody4.wav 120_Bb_guit1.wav 120_Bb_piano1.wav

In diesen Dateinamen sind einige Informationen enthalten. Zuerst haben wir die BPM (“beats per minute”) des Samples. D.h. das Piano Sample hat 120 BPM und unsere ersten drei Melodien haben 100 BPM. Außerdem steht in den Samplenamen die Tonart. Das Gitarren-Sample ist in Bb und die Melodien sind in A#. Diese Information ist für das Mixen der Samples in unserem Code sehr wichtig. Zum Beispiel wissen wir, dass wir das Piano-Sample nur mit Code verwenden können, der 120 BPM und die Tonart Bb hat.

Es stellt sich heraus, dass wir dies spezielle Namensgebung unserer Sample-Sets im Code nutzen können, um herauszufiltern, was wir wollen. Wenn wir zum Beispiel mit 120 BPM arbeiten, können wir mit folgendem Code alle Samples filtern, deren Name den String “120” enthält:

samps = "/pfad/zu/meinen/samples/"
sample samps, "120"

Dieser Code wird das erste passende Sample abspielen. Wenn wir das zweite passende Sample abspielen wollen, müssen wir nur einen Index nutzen:

samps = "/pfad/zu/meinen/samples/“
sample samps, "120", 1

Wir können sogar mehrere Filter gleichzeitig nutzen. Der folgende Code findet für uns ein Sample, dessen Name sowohl den String “120” als auch den String “A#” enthält:

samps = "/pfad/zu/meinen/samples/"
sample samps, "120", "A#"

Schließlich können wir auch noch unsere üblichen Optionen beim Abspielen des Samples anhängen:

samps = "/pfad/zu/meinen/samples/"
sample samps, "120", "Bb", 1, lpf: 70, amp: 2

Quellen

Das Filter-System für Samples versteht zwei Typen von Informationen: Quellen und Filter. Quellen bieten Informationen, um eine Liste möglicher Kandidaten zu versammeln. Eine Quelle kann in zwei Formen angegeben werden:

“/pfad/zu/samples” - eine Zeichenkette, die einen gültigen Pfad zu einem Verzeichnis angibt “/pfad/zu/samples/foo.wav” - eine Zeichenkette, die einen gültigen Pfad zu einem Sample angibt

Der sample-Befehl sammelt alle Quellen und erzeugt daraus eine lange Liste von Kandidaten. Für diese Liste durchsucht er zunächst alle gültigen Verzeichnispfade und fügt dann alle darin enthaltenen Dateien in den Formaten .flac, .aif, .aiff, .wav, .wave hinzu.

Schau Dir z. B. den folgenden Code an:

samps = "/pfad/zu/meinen/samples/"
samps2 = "/pfad/zu/meinen/samples2/"
path = "/pfad/zu/meinen/samples3/foo.wav"
sample samps, samps2, path, 0

Hier sammeln wir alle Samples aus zwei Verzeichnissen und fügen noch einen einzelnen Sample hinzu. Wenn "/pfad/zu/meinen/samples/" 3 Samples und "/path/to/my/samples2/" 12 Samples enthielte, hätten wir insgesamt 16 mögliche Samples, die wir indexieren und filtern können (3 + 12 + 1).

Normalerweise werden nur die Sample-Dateien des angegebenen Verzeichnisses in der Kandidatenliste versammelt. Manchmal möchtest Du vielleicht eine ganze Reihe ineinander verschachtelter Ordner mit Samples durchsuchen und filtern. Das kannst Du mit einer rekursiven Suche nach allen Samples in allen Unterordnern erreichen, indem Du ** an das Ende des Pfads stellst:

samps = "/path/to/nested/samples/**"
sample samps, 0

Sei aber vorsichtig, weil eine Suche über viele Verzeichnisse hinweg lange dauern kann. Allerdings wird der Inhalt aller Ordner, die Du als Quellen angegeben hast, zwischengespeichert; die Suche wird also nur beim ersten Mal langsam sein.

Ein wichtiger Punkt zum Abschluss: Die Quelle muss als erstes angegeben werden. Wenn Du keine Quelle angibst, werden die mitgelieferten Samples für die Standardliste ausgewählt, mit der Du dann weiter arbeitest.

Filter

Wenn Du einmal die Kandidatenliste hast, kannst du die folgenden Filtertypen zur weiteren Auswahl benutzen:

"foo" Filtert alle Dateinamen, in denen diese Zeichenkette vorkommt (ohne den Verzeichnispfad und die Dateiendung). /fo[oO]/ Filtert alle Dateinamen, die diesem regulären Ausdruck entsprechen (ohne den Verzeichnispfad und die Dateiendung). :foo - Filtert alle Kandidaten, wo dieses Kennwort genau dem Dateinamen entspricht (ohne den Verzeichnispfad und die Dateiendung). lambda{|a| ... } - Procs mit einem Argument werden als Filter für Kandidaten oder Generator-Funktionen behandelt. Diesem wird die Liste der aktuellen Kandidaten übergeben und er gibt eine neue Liste von Kandidaten zurück (eine Liste gültiger Pfade zu Sample-Dateien). 1 - Zahlen wählen direkt den Kandidaten aus, dessen Index dieser Zahl entspricht (wenn nötig wird wie bei einem Ring von vorne begonnen).

Wir können z. B. alle Samples in einem Verzeichnis herausfiltern, deren Dateinamen die Zeichenkette "foo" enthalten, und davon das erste passende Sample mit halber Geschwindigkeit spielen:

sample "/path/to/samples", "foo", rate: 0.5

Sieh’ Dir dazu den Hilfeeintrag zu sample mit vielen Anwendungsbeispielen an. Achte darauf, dass die Sortierung der Filter eine Rolle spielt.

Zusammengesetzte Ausdrücke

Schließlich: Du kannst eine Liste verwenden, wo immer Du eine Quelle oder einen Filter anwendest. Die Liste wird automatisch aufgelöst und ihr Inhalt so behandelt, als wäre er eine normale Quellen- oder Filterangabe. Deshalb bedeuten die folgenden Aufrufe von sample alle dasselbe:

sample "/path/to/dir", "100", "C#"
sample ["/path/to/dir", "100", "C#"]
sample "/path/to/dir", ["100", "C#"]
sample ["/path/to/dir", ["100", ["C#"]]]

Zum Abschluss

Das war ein komplizierter Abschnitt für alle, die tiefer in Sample-Pakete einsteigen möchten. Mach’ Dir keine Sorgen, wenn vieles von dem, was hier steht, für Dich im Moment noch wenig Sinn macht. Es ist gut möglich, dass Du davon vieles noch nicht brauchst. Dennoch, Du wirst schon merken, wenn Du es brauchst, und dann kannst Du zurückkommen und den Abschnitt noch einmal lesen, wenn Du einmal mit umfangreichen Sample-Sammlungen hantieren möchtest.


4 - Randomisierung

Zufallszahlen sind eine tolle Möglichkeit, Deine Musik interessant zu gestalten. In Sonic Pi sind einige Funktionen enthalten, um Zufallsfaktoren in Deine Musik einzubauen. Aber bevor wir starten, müssen wir noch einer schockierenden Wahrheit ins Gesicht sehen: In Sonic Pi bedeutet zufällig nicht wirklich zufällig. Was zum Teufel soll das bedeuten? Nun, das verrate ich Dir jetzt.

Wiederholbarkeit

Eine wirklich nützliche Zufallsfunktion ist rrand. Sie liefert Dir einen zufälligen Wert zwischen zwei Zahlen - einem Minimal- und einem Maximalwert. (rrand ist ein Kürzel für das englische ranged random, also eine Zufallszahl in einem bestimmten Wertebereich.) Hier eine zufällig ausgewählte Note:

play rrand(50, 95)

Oh, eine zufällige Note wird gespielt. Es war die Note 83.7527. Eine nette Note zwischen 50 und 100. Aber hallo, habe ich gerade diese angeblich zufällige Note exakt vorhergesagt? Da ist doch etwas nicht ganz astrein. Lasse den Code noch einmal ablaufen. Wieder 83.7527, oder? Das kann doch kein Zufall sein!

Die Antwort ist, es ist nicht wirklich zufällig, sondern pseudo-zufällig. Sonic Pi liefert Dir Reihenfolgen von Zufallszahlen, die wiederholbar sind. Das ist sehr nützlich, denn so ist sichergestellt, dass die Musik von Deinem Rechner auf anderen Rechnern identisch klingt - sogar dann, wenn Du den Zufallsgenerator einbaust.

Klar, wenn in einem bestimmten Musikstück jedesmal die 83.7527 als ‘zufällige’ Zahl gewählt würde, dann wäre das nicht besonders interessant. Aber so ist es auch nicht. Versuch folgendes:

loop do
  play rrand(50, 95)
  sleep 0.5
end 

Jawohl! Nun klingt es zufällig. Innerhalb eines bestimmten Code-Durchgangs liefern Aufrufe von Zufallsfunktionen auch zufällige Werte. Der nächste Durchgang wird jedoch genau die selbe Folge von Zufallswerten liefern und also auch genau gleich klingen. Es ist, als ob der Code immer zu demselben Zeitpunkt zurückspringt, wenn der Ausführen-Button geklickt wird. Es ist der Murmeltier-Tag der musikalischen Synthese!

Ruhelose Glocken

Ein großartiges Beispiel von Zufall in Aktion bietet der “Haunted Bells”-Code. Die “ruhelosen Glocken” spielen das Sample :perc_bell mit einer zufälligen Samplerate und Pausenzeit in einer Endlosschleife ab:

loop do
  sample :perc_bell, rate: (rrand 0.125, 1.5)
  sleep rrand(0.2, 2)
end

Zufällig abschneiden (random cutoff)

Ein anderes spannendes Beispiel für die Randomisierung ist das zufällige Ausfiltern bzw. Abschneiden hoher Töne eines Synth-Klangs. Der :tb303-Emulator ist ein guter Synth, um das auszuprobieren:

use_synth :tb303
loop do
  play 50, release: 0.1, cutoff: rrand(60, 120)
  sleep 0.125
end

Startpunkt der Zufallsfolge (random seed)

Was aber, wenn Du die Abfolge von Zufallszahlen, die Sonic Pi Dir liefert, nicht magst? Nun, mit use_random_seed kannst Du unterschiedliche Startpunkte für diese Folge angeben. Der Standard-Startpunkt ist die 0. Wähle also einfach einen anderen Startpunkt und mache eine andere Zufallserfahrung!

Sieh Dir den folgenden Code an:

5.times do
  play rrand(50, 100)
  sleep 0.5
end

Jedes Mal, wenn Du den Code ablaufen lässt, hörst Du dieselbe Folge von 5 Tönen. Um eine andere Folge zu bekommen, setze einfach einen anderen Startpunkt:

use_random_seed 40
5.times do
  play rrand(50, 100)
  sleep 0.5
end

Nun produziert Sonic Pi eine andere Folge von 5 Tönen. Indem Du den Startpunkt wechselst und Dir die Ergebnisse anhörst, kannst Du eine Folge finden, die Dir gefällt - und wenn Du den Code dann an andere weitergibst, werden sie genau das hören, was auch Du gehört hast.

Schauen wir uns noch eine andere nützliche Zufallsfunktion an.

Auswählen (choose)

Häufig kommt es vor, dass man aus einer Liste von Dingen eines zufällig auswählen möchte. Zum Beispiel möchte ich einen Ton aus der folgenden Liste auswählen: 60, 65 oder 72. Dafür ist choose da. Zuerst musst Du Deine Zahlen in eine Liste packen. Dafür schreibst Du sie jeweils durch Kommata getrennt in eckige Klammern. Dann übergibst Du diese Liste dem Kommando choose:

choose([60, 65, 72])

Hören wir uns das an:

loop do
  play choose([60, 65, 72])
  sleep 1
end

rrand

rrand haben wir schon kennengelernt, aber sehen wir uns das noch einmal genauer an. Es liefert eine zufällige Zahl zwischen zwei Werten, aber ohne diese Werte selbst; man sagt auch exklusiv dieser beiden Werte. Das bedeutet, dass sowohl der minimale als auch der maximale Wert niemals ausgegeben werden, immer nur eine Zahl zwischen diesen beiden Werten. Die Zahl wird immer eine Gleitkommazahl sein, also keine ganze Zahl, sondern eine mit einem Komma. (Ok, wir schreiben sie mit einem Komma, aber im englischen Sprachraum heißen sie Floating Point - oder kurz: Float - und man schreibt sie mit einem Punkt.) Einige Beispiele für Gleitkommazahlen, die der wiederholte Aufruf von rrand(20, 110) ausgeben könnte:

87.5054931640625 86.05255126953125 61.77825927734375

rrand_i

Manchmal braucht man eine zufällige, aber ganze Zahl, eben keine Gleitkommazahl. Hier rettet einen rrand_i. (Das “i” steht hier für das englische Wort Integer, also ganze Zahl.) Es funktioniert ähnlich rrand, kann jedoch auch den minimalen oder maximalen Wert, den man übergeben hat, als mögliche Zufallszahl auswählen (man kann auch sagen: es ist inklusiv, also nicht exklusive der Werte, mit denen man den Bereich für die Auswahl festgelegt hat). rrand_i(20, 110) könnte zum Beispiel die folgenden Werte ausgeben:

88 86 62

rand

rand gibt eine zufällige Gleitkommazahl zwischen 0 (inklusiv) und einem übergebenen Maximalwert (exklusiv) zurück. Standardmäßig - wenn also kein Maximalwert angegeben wird - wird ein Wert zwischen 0 und 1 geliefert. Deshalb kann man rand gut dafür gebrauchen, zufällige Werte für amp: (also die Lautstärke) auszuwählen:

loop do
  play 60, amp: rand
  sleep 0.25
end

rand_i

Ähnlich wie bei rrand_i und rrand, wählt rand_i eine zufällige ganze Zahl zwischen 0 und dem angegebenen Maximalwert aus.

dice

Manchmal möchte man so tun, als würde man würfeln (engl. to dice) - das ist ein Sonderfall von rrand_i, wobei der kleinste Wert immer die 1 ist. Wenn man dice verwendet, muss man dabei immer bestimmen, wie viele Seiten der Würfel hat. Ein normaler Würfel hat 6 Seiten, also wird dice(6) entsprechend funktionieren und den Wert 1, 2, 3, 4, 5 oder 6 zurückgeben. Aber - angenommen wir befänden uns in einen Fantasy-Rollenspiel - ist es Dir vielleicht lieber, wenn der Würfel 4 oder 12, 20 oder sogar 120 Seiten hat?

one_in

Schließlich könnte es sein, dass Du so tun willst, als ob Du beim Würfeln eine 6 hast - also den höchten Wert erreichst. one_in gibt - mit einer Wahrscheinlichkeit 1 im Verhältnis zur Menge der Würfelseiten - den Wert wahr (engl. true) zurück, falls die höchste Zahl gewürfelt wurde. one_in(6) wird also mit einer Wahrscheinlichkeit von 1 zu 6 wahr, ansonsten falsch (engl. false). Wahr- und Falsch-Werte sind sehr nützlich, wenn es um if-Anweisungen geht, die wir in einem späteren Kapitel dieses Tutorials besprechen.

Jetzt los, bring Deinen Code mit ein paar Zufälligkeiten durcheinander!


5 - Programmstrukturen

Jetzt kennst Du schon die wichtigsten Grundlagen. Du weißt, wie man Klänge mit play und sample erzeugt, und kannst einfache Melodien und Rhythmen bauen, indem Du sleep zwischen die Klänge setzt. Du fragst Dich nun vielleicht, was Dir die Welt des Codes noch alles zu bieten hat…

Ich denke, da kommen noch einige tolle Sachen auf Dich zu! Du wirst sehen, dass grundlegende Programmstrukturen wie Schleifen, Bedingungen, Funktionen und Threads großartige Werkzeuge sind, wenn Du Deine musikalischen Ideen ausdrücken willst.

Sehen wir uns also die Grundlagen an…


5.1 - Blocks

Eine Struktur, die man häufig in Sonic Pi sieht, ist der Block. Blocks erlauben uns, nützliche Dinge mit größeren Codeabschnitten zu tun. Mit Synth- oder Sample-Parametern konnten wir etwas ändern, was in einer Codezeile geschah. Manchmal aber möchten wir etwas mit einer ganzen Reihe von Codezeilen anstellen. Zum Beispiel möchten wir diese in einer Schleife ablaufen lassen, Hall hinzufügen, bei vorgegebenen Wiederholungen nur eine von fünf ablaufen lassen etc. Schau Dir den folgenden Code an:

play 50
sleep 0.5
sample :elec_plip
sleep 0.5
play 62

Um etwas mit einem mehrzeiligen Codestück zu tun, müssen wir Sonic Pi sagen, wo der Code-Block anfängt, und wo er aufhört. Dazu verwenden wir do für den Anfang und end für das Ende. So zum Beispiel:

do
  play 50
  sleep 0.5
  sample :elec_plip
  sleep 0.5
  play 62
end

Das ist aber noch nicht alles und wird so nicht funktionieren (versuche es und Du wirst einen Fehler erhalten), weil wir Sonic Pi noch nicht gesagt haben, was wir mit diesem do/end-Block machen wollen. Dafür setzen wir noch einen speziellen Ausdruck vor das do. Solche Ausdrücke werden wir in diesem Tutorial gleich kennenlernen. Zunächst einmal ist wichtig, dass wir mehrere Codezeilen mit do und end zusammenfassen und Sonic Pi sagen können, dass wir mit diesem Codestück etwas Bestimmtes machen wollen.


5.2 - Iteration und Schleifen

Bislang haben wir einige Zeit damit verbracht, uns unterschiedliche Klänge anzusehen, die Du mit play und sample erzeugen kannst. Wir haben auch gelernt, wie Du den Ablauf dieser Klänge mit sleep steuern kannst.

Du hast ja schon entdeckt, dass man mit diesen Grundbausteinen schon vieles bauen kann. Eine ganz neue Dimension eröffnet sich jedoch, wenn wir beginnen, die Musik und die Kompositionen über den Code weitergehend zu strukturieren. In den nächsten Abschnitten schauen wir einige wirkungsvolle neue Werkzeuge dafür an. Als erstes kommen die Iteration und die Schleife (engl. Loop) dran.

Wiederholung

Möchtest Du Code, den Du geschrieben hast, einige Male wiederholen? Vielleicht hast Du z.B. ungefähr so etwas:

play 50
sleep 0.5
sample :elec_blup
sleep 0.5
play 62
sleep 0.25

Was, wenn Du das drei Mal wiederholen wolltest? Naja, die Lösung ist einfach! Du kannst den Code kopieren und drei Mal hintereinander einfügen:

play 50
sleep 0.5
sample :elec_blup
sleep 0.5
play 62
sleep 0.25
play 50
sleep 0.5
sample :elec_blup
sleep 0.5
play 62
sleep 0.25
play 50
sleep 0.5
sample :elec_blup
sleep 0.5
play 62
sleep 0.25

Das ist natürlich eine Menge Code! Was wäre, wenn Du das Sample in :elec_plip umändern wolltest? Du müsstest alle Stellen mit :elec_blub finden und ändern. Und weiter: Was, wenn Du das ursprüngliche Codestück 50 oder 1000 Mal wiederholen wolltest? Das wäre noch mehr Code, und Du müsstest eine Menge Zeilen bearbeiten, wenn Du etwas ändern wolltest.

Iteration

Wenn man Code wiederholt ablaufen lassen wollte, müsste man eigentlich nur sagen: Mach das drei Mal. Und so ist es auch in etwa. Erinnerst Du Dich an unseren Freund, den Code-Block? Wir können damit den Anfang und das Ende des Codes markieren, den wir drei Mal wiederholen wollen. Dann benutzen wir den speziellen Ausdruck 3.times. Wir schreiben zwar nicht mach das drei Mal, wir schreiben 3.times do - also nicht besonders schwierig. Denk aber daran, ans Ende des zu wiederholenden Code-Blocks gehört ein end:

3.times do
  play 50
  sleep 0.5
  sample :elec_blup
  sleep 0.5
  play 62
  sleep 0.25
end

Na, ist das nicht viel eleganter als kopieren und einfügen? Wir können bei vielen sich wiederholenden Strukturen gut gebrauchen:

4.times do
  play 50
  sleep 0.5
end
8.times do
  play 55, release: 0.2
  sleep 0.25
end
4.times do
  play 50
  sleep 0.5
end

Verschachtelte Iterationen

Wenn wir Iterationen in andere Iterationen hinein packen, entstehen interessante Muster. Zum Beispiel:

4.times do
  sample :drum_heavy_kick
  2.times do
    sample :elec_blip2, rate: 2
    sleep 0.25
  end
  sample :elec_snare
  4.times do
    sample :drum_tom_mid_soft
    sleep 0.125
  end
end

Schleifen

Wenn Du etwas sehr oft wiederholen möchtest, könnte es sein, dass Du mit sehr hohen Zahlen arbeiten musst, wie zum Beispiel 1000.times do. In einem solchen Fall macht es mehr Sinn, Sonic Pi dazu zu bringen, den Code in einer Endlos-Schleife zu wiederholen (zumindest, bis Du den Stopp-Button klickst!). Wiederholen wir den Amen-Break unendlich oft:

loop do
  sample :loop_amen
  sleep sample_duration :loop_amen
end

Eine wichtige Sache bei Endlos-Schleifen ist, dass sie für den Code wie schwarze Löcher sind. Wenn der Code einmal in einer Endlos-Schleife läuft, kann er sie nicht mehr verlassen, bis Du auf Stopp klickst - ansonsten wird er für immer weiterlaufen. Das bedeutet, wenn hinter Deiner Endlos-Schleife noch weiterer Code steht, wirst Du diesen nie hören. Zum Beispiel wird das Becken hinter dieser Schleife nie spielen:

loop do
  play 50
  sleep 1
end
sample :drum_cymbal_open

Jetzt fang an und strukturiere Deinen Code mit Wiederholungen und Schleifen!


5.3 - Bedingungen

Wahrscheinlich willst Du häufig nicht nur einen einen zufälligen Ton abspielen (siehe das Kapitel über Zufallszahlen), sondern auch eine zufällige Entscheidung treffen: je nachdem wie diese ausfällt, möchtest Du dann den einen oder anderen Code ausführen. Zum Beispiel könnte es sein, dass Du zufällig entweder eine Trommel oder ein Becken anschlagen möchtest. Das können wir mit einem if-Ausdruck erreichen.

Wirf eine Münze

Werfen wir also ein Münze: bei Kopf, spiele eine Trommel, bei Zahl, ein Becken. Das ist leicht. Wir können den Münzwurf mit der one_in-Funktion nachbilden (eingeführt im Kapitel über Randomisierung), indem wir eine Wahrscheinlichkeit von 1 aus 2 angeben: one_in(2). Das Ergebnis dieser Berechnung benutzen wir, um zwischen zwei Codestücken auszuwählen, entweder den Code für die Trommel oder den für das Becken:

loop do
  if one_in(2)
    sample :drum_heavy_kick
  else
    sample :drum_cymbal_closed
  end
  
  sleep 0.5
  
end

Denke daran, dass if-Anweisungen drei Teile haben:

Die Frage, die gestellt wird, die erste Wahl des Codes, der ablaufen soll (wenn die Antwort auf die Frage ein Ja ist), die zweite Wahl des Codes, der ablaufen soll (wenn die Antwort auf die Frage ein Nein ist).

Für Programmiersprachen typisch steht für das Ja der Ausdruck true (wahr) und für das Nein der Ausdruck false (falsch). Also müssen wir eine Frage finden, die uns eine Antwort gibt, die entweder wahr oder falsch ist; genau das erledigt hier im Beispiel one_in.

Beachte, dass die erste Wahl zwischen if und else und die zweite Wahl zwischen else und end eingeschlossen wird. So wie bei den do/end-Blöcken kannst Du beliebig viele Codezeilen an beide Stellen schreiben. Zum Beispiel:

loop do
  if one_in(2)
    sample :drum_heavy_kick
    sleep 0.5
  else
    sample :drum_cymbal_closed
    sleep 0.25
  end
  
end

Dieses Mal schläft der Code unterschiedlich lange, je nachdem, welche Auswahl getroffen wird.

Einfaches if

Manchmal möchtest Du wahlweise nur eine Codezeile ausführen. Dafür hängst Du ein if und die Frage an das Ende der Codezeile, zum Beispiel:

use_synth :dsaw
loop do
  play 50, amp: 0.3, release: 2
  play 53, amp: 0.3, release: 2 if one_in(2)
  play 57, amp: 0.3, release: 2 if one_in(3)
  play 60, amp: 0.3, release: 2 if one_in(4)
  sleep 1.5
end

Dieses Beispiel setzt einen Akkord aus verschiedenen Noten zusammen, wobei die einzelnen Noten jeweils eine unterschiedliche Wahrscheinlichkeit haben.


5.4 - Threads

Mal angenommen, Du hast eine Killer-Basslinie und einen krassen Beat gebaut. Wie kannst Du beides zur selben Zeit spielen lassen? Eine Möglichkeit ist es, beide Sounds per Hand ineinander zu weben - erst spielt der Bass ein bisschen, dann das Schlagzeug, dann der Bass etwas mehr… Beides aufeinander zeitlich abzustimmen wird jedoch immer schwieriger, vor allem, wenn noch mehr Sounds dazukommen sollen.

Was, wenn Sonic Pi Sounds automatisch für Dich ineinander flechten könnte? Kann es auch, und zwar mit einem besonderen Ding, welches Thread heißt.

Unendliche Schleifen

Damit das nächste Beispiel nicht zu kompliziert wird, musst Du Dir einfach vorstellen, dass dies Deine Killer-Basslinie und Dein krasser Beat sind:

loop do
  sample :drum_heavy_kick
  sleep 1
end
loop do
  use_synth :fm
  play 40, release: 0.2
  sleep 0.5
end

Wir haben das früher schon besprochen, Endlos-Schleifen sind wie schwarze Löcher für ein Programm; läuft es einmal in die Schleife kommt es da nicht mehr raus, bis Du den Stopp-Button klickst. Wie also können wir beide Schleifen zur selben Zeit abspielen? Wir müssen Sonic Pi sagen, dass wir einen bestimmten Abschnitt gleichzeitig mit dem Rest des Codes starten möchten. Hierbei helfen uns Threads.

Threads kommen zur Hilfe

in_thread do
  loop do
    sample :drum_heavy_kick
    sleep 1
  end
end
loop do
  use_synth :fm
  play 40, release: 0.2
  sleep 0.5
end

Wenn wir die erste Schleife in einen in_thread-do/end-Block hinein packen, sagen wir Sonic Pi, es soll den Inhalt dieses do/end-Blocks genau zur selben Zeit wie die nachfolgende Anweisung ausführen. Und das ist in diesem Fall die zweite Schleife. Probier es aus, und Du wirst den Beat zusammen mit der Basslinie hören!

Angenommen, dass wir obendrauf noch einen Synth legen wollten. Ungefähr so:

in_thread do
  loop do
    sample :drum_heavy_kick
    sleep 1
  end
end
loop do
  use_synth :fm
  play 40, release: 0.2
  sleep 0.5
end
loop do
  use_synth :zawa
  play 52, release: 2.5, phase: 2, amp: 0.5
  sleep 2
end

Jetzt haben wir das gleiche Problem wie vorhin. Die erste Schleife wird durch das in_thread zur selben Zeit wie die zweite gespielt. Aber die dritte Schleife wird nie erreicht. Also brauchen wir einen weiteren Thread:

in_thread do
  loop do
    sample :drum_heavy_kick
    sleep 1
  end
end
in_thread do
  loop do
    use_synth :fm
    play 40, release: 0.2
    sleep 0.5
  end
end
loop do
  use_synth :zawa
  play 52, release: 2.5, phase: 2, amp: 0.5
  sleep 2
end

Ablauf in Threads

Was Dich vielleicht erstaunt: Wenn Du den Ausführen-Button klickst, erzeugst Du tatsächlich einen neuen Thread, innerhalb dessen der Code abläuft. Deshalb entstehen immer neue Soundschichten, wenn Du den Ausführen-Button wiederholt klickst. Weil die Abläufe Threads sind, werden sie automatisch die Sounds verflechten.

Geltungsbereich (Scope)

Wenn Du Dich besser mit Sonic Pi auskennst, wirst Du herausfinden, dass Threads die wichtigsten Bausteine für Deine Musik sind. Threads können die aktuellen Einstellungen, die für einen Thread gelten, von anderen Threads isolieren. Was genau bedeutet das? Wenn Du etwa einen Synths mit use_synth durch einen anderen austauschst, dann veränderst Du den Synth lediglich für den aktuellen Thread - kein anderer der laufenden Threads bekommt den neuen Synth. Sehen wir uns das mal in Aktion an:

play 50
sleep 1
in_thread do
  use_synth :tb303
  play 50
end
sleep 1
play 50

Hast Du gehört, dass sich der mittlere Klang von den anderen beiden unterschieden hat? Die use_synth-Anweisung hat sich nur auf den Thread ausgewirkt, in dem sie auch stand, aber nicht auf den äußeren Haupt-Thread.

Vererbung

Wenn Du einen neuen Thread mit in_thread erzeugst, wird dieser vom vorherigen alle Einstellungen automatisch erben. Sehen wir uns das an:

use_synth :tb303
play 50
sleep 1
in_thread do
  play 55
end

Hast Du bemerkt, dass der zweite Ton mit dem :tb303-Synth gespielt wird, obwohl er in einem anderen Thread läuft? Jede der Einstellungen, die mit den unterschiedlichen use_*-Ausdrücken vorgenommen wird, verhält sich genauso.

Wenn neue Threads starten, erben sie alle Einstellungen von ihren Eltern. Wenn Du aber Einstellungen innerhalb dieser neuen Threads änderst, haben diese keine Einfluss auf die Eltern-Threads.

Threads benennen

Schließlich kannst Du Deinen Threads Namen geben:

in_thread(name: :bass) do
  loop do
    use_synth :prophet
    play chord(:e2, :m7).choose, release: 0.6
    sleep 0.5
  end
end
in_thread(name: :drums) do
  loop do
    sample :elec_snare
    sleep 1
  end
end

Sieh Dir das Protokoll-Fenster an, wenn Du diesen Code laufen lässt. Siehst Du, wie die Namen der Threads ausgeben werden?

[Run 36, Time 4.0, Thread :bass]
 |- synth :prophet, {release: 0.6, note: 47}

Nur ein Name pro Thread erlaubt

Eine letzte Anmerkungen zu Threads mit Namen: Es können nicht zwei gleichzeitig laufende Threads den gleichen Namen haben. Probieren wir das aus. Sieh Dir den folgenden Code an:

in_thread do
  loop do
    sample :loop_amen
    sleep sample_duration :loop_amen
  end
end

Kopiere das einmal in einen Puffer und klicke den Ausführen-Button. Klick in noch ein paar mal. Hör Dir diese Kakophonie mehrerer Amen-Breaks an, die rhythmisch nicht unbedingt passend zueinander ablaufen. Ok, jetzt kannst Du auf Stopp klicken.

Dieses Verhalten konnten wir schon woanders sehen - wenn Du den Ausführen-Button klickst, legen sich die neuen Klänge über die schon laufenden. Wenn eine Schleife abläuft, und Du den Ausführen-Button dreimal klickst, bekommst Du drei Ebenen mit Schleifen, die gleichzeitig ablaufen.

Mit benannten Threads ist das jedoch anders:

in_thread(name: :amen) do
  loop do
    sample :loop_amen
    sleep sample_duration :loop_amen
  end
end

Versuche mit diesem Code den Ausführen-Button mehrmals zu klicken. Du wirst immer nur eine Amen-Break-Schleife hören. Das kannst Du auch im Protokoll sehen:

==> Skipping thread creation: thread with name :amen already exists.

Sonic Pi sagt Dir, dass ein Thread mit dem Namen :amen bereits läuft und es deshalb keinen weiteren startet.

Vielleicht erscheint Dir dieses Verhalten im Moment noch nicht sinnvoll - aber es wird sehr nützlich sein, wenn wir ins Live-Coding einsteigen…


5.5 - Funktionen

Wenn Du einmal damit angefangen hast, mehr Code zu schreiben, dann wirst Du nach Wegen suchen, wie Du die Dinge organisieren und strukturieren kannst. Damit wird alles ordentlicher und einfacher zu verstehen. Funktionen sind dafür sehr praktisch. Sie geben uns die Möglichkeit, einem Bündel von Codezeilen einen Namen zu geben. Sehen wir uns das an.

Funktionen definieren

define :foo do
  play 50
  sleep 1
  play 55
  sleep 2
end

Hier haben wir eine neue Funktion mit dem Namen foo definiert. Wir machen das mit unserem alten Freund, dem do/end-Block, und dem Zauberwort define gefolgt von dem Namen, den wir unserer Funktion geben möchten. Wir müssen die Funktion nicht unbedingt foo nennen, wir können sie auch irgendwie anders nennen; zum Beispiel bar, baz oder idealerweise einen für Dich bedeutsamen Namen wie erste_strophe oder hintergrund_akkorde.

Denk daran, bei der Definition einer Funktion einen Doppelpunkt : vor ihren Namen zu stellen.

Funktionen aufrufen

Wenn wir unsere Funktion definiert haben, können wir sie einfach über ihren Namen aufrufen:

define :foo do
  play 50
  sleep 1
  play 55
  sleep 0.5
end
foo
sleep 1
2.times do
  foo
end

Wir können foo sogar in Iterations-Blocks aufrufen und überall da, wo wir auch play oder sample hätten benutzen können. Das gibt uns eine große Ausdrucksfreiheit und wir können sinnvolle Worte bilden, die wir in unseren Kompositionen verwenden.

Funktionen bleiben in Erinnerung

Wenn Du bislang den Ausführen-Button geklickt hast, startete Sonic Pi jedes mal aufs Neue ohne irgendwelche Vorgaben. Es berücksichtigt nichts, außer dem, was im jeweiligen Puffer steht. Du kannst Dich nicht auf irgendwelchen Code beziehen, der in einem anderen Puffer oder einem anderen Thread steht. Funktionen ändern das jedoch. Wenn Du eine Funktion definierst, dann erinnert sich Sonic Pi daran. Probieren wir das aus. Ersetze den gesamten Code in Deinem Puffer durch:

foo

Klick den Ausführen-Button und höre, wie Deine Funktion spielt. Wo wurde dieser Code gespeichert? Woher weiß Sonic Pi, was es zu spielen hat? Sonic Pi hat sich Deine Funktion einfach gemerkt. Also sogar, nachdem Du den Code aus dem Puffer gelöscht hast, wusste Sonic Pi noch, was Du geschrieben hattest. Dies funktioniert nur mit Funktionen, die Du mit define (und defonce) definiert hast.

Funktionen parametrisieren

Du kannst Deinen Funktionen beibringen, Argumente zu übernehmen, genau so, wie Du z.B. rrand einen Minimal- und einen Maximalwert übergeben kannst. Sehen wir uns das an:

define :my_player do |n|
  play n
end
my_player 80
sleep 0.5
my_player 90

Das ist jetzt nicht besonders aufregend, zeigt aber, worum es hier geht. Wir haben unsere eigene Version von play mit dem Namen my_player erschaffen. Diese ist parametrisiert - sie akzeptiert also Argumente.

Die Parameter müssen nach dem do stehen, welches zum define des do/end-Blocks gehört; sie werden von senkrechten Strichen umgeben und durch Kommata getrennt. Du kannst beliebige Worte als Parameternamen verwenden.

Die Zauberei findet innerhalb des define-do/end-Blocks statt. Du kannst die Parameternamen so benutzen, als wären sie wirkliche Werte. In diesem Beispiel spiele ich den Ton n. Du kannst die Parameter als eine Art Versprechen ansehen, dass sie durch wirkliche Werte ersetzt werden, wenn der Code läuft. Das machst Du, indem Du der Funktion beim Aufruf einen Parameter mitgibst. Ich tue das hier mit my_player 80, um den Ton 80 zu spielen. Innerhalb der Funktionsdefinition wird n nun durch 80 ersetzt, sodass play n sich in play 80 verwandelt. Wenn ich die Funktion erneut mit my_player 90 aufrufe, wird n durch 90 ersetzt, sodass sich play n in play 90 verwandelt.

Sehen wir uns interessantere Beispiele an:

define :chord_player do |root, repeats| 
  repeats.times do
    play chord(root, :minor), release: 0.3
    sleep 0.5
  end
end
chord_player :e3, 2
sleep 0.5
chord_player :a3, 3
chord_player :g3, 4
sleep 0.5
chord_player :e3, 3

Hier habe ich repeats so benutzt, als ob es eine Zahl in der Zeile repeats.times do wäre. Zusätzlich habe ich roots so verwendet, als ob es ein Notenname im Aufruf play wäre.

Siehst Du? Unser Code wird sehr aussagekräftig und leichter lesbar, wenn wir eine Menge der Programmlogik in Funktionen verschieben!


5.6 - Variablen

Beim Coden ist es sehr nützlich, Namen für Dinge zu vergeben. Sonic Pi macht es Dir hier sehr leicht: Schreib einfach den Namen, den Du gerne verwenden möchtest, dann ein Gleichheitszeichen (=) und dann das Ding, welches Du Dir merken möchtest:

sample_name = :loop_amen

Hier haben wir uns das Symbol :loop_amen mit der Variable sample_name gemerkt. Wir können nun sample_name überall da benutzen, wo wir auch loop_amen benutzen könnten. Zum Beispiel:

sample_name = :loop_amen
sample sample_name

Es gibt drei Hauptgründe, Variablen in Sonic Pi zu nutzen: Bedeutung vermitteln, Wiederholung steuern und Ergebnisse speichern.

Bedeutung vermitteln

Wenn Du Code schreibst, könntest Du denken, dass Du einfach nur dem Computer sagst, was er tun soll - solange der Computer das versteht, ist es okay. Vergiss aber nicht, dass nicht nur ein Computer Deinen Code lesen will. Andere Leute könnten den Code auch lesen und wollen verstehen, was da vor sich geht. Wahrscheinlich wirst Du den Code auch später selbst wieder lesen und möchtest verstehen, was er bedeutet. Obwohl Dir jetzt vielleicht alles offensichtlich erscheint - wahrscheinlich ist es für andere nicht ganz so offensichtlich und vielleicht nicht einmal für Dich in der Zukunft!

Mit Kommentaren hilfst Du anderen, Deinen Code zu verstehen. Und Du kannst sinnvolle Namen für Deine Variablen verwenden. Sie Dir diesen Code an:

sleep 1.7533

Warum steht hier die Zahl 1.7533. Woher kommt diese Zahl? Was bedeutet sie? Sieh Dir zum Vergleich diesen Code an:

loop_amen_duration = 1.7533
sleep loop_amen_duration

Aha, jetzt ist viel klarer, was 1.7533 bedeutet: Es ist die Dauer des Sample :loop_amen! Natürlich kannst Du jetzt sagen, warum nicht einfach schreiben:

sleep sample_duration(:loop_amen)

Das ist natürlich auch ein sehr guter Weg, die Absicht hinter dem Code mitzuteilen.

Wiederholungen steuern

In Deinem Code wirst Du oft Dinge wiederholen, und wenn Du irgendwo etwas ändern willst, musst Du das an vielen Stellen tun. Schau Dir mal diesen Code an:

sample :loop_amen
sleep sample_duration(:loop_amen)
sample :loop_amen, rate: 0.5
sleep sample_duration(:loop_amen, rate: 0.5)
sample :loop_amen
sleep sample_duration(:loop_amen)

Hier machen wir eine ganze Menge mit dem :loop_amen! Was wäre, wenn Du das Ganze mit einem anderen Loop-Sample wie zum Beispiel :loop_garzul hören wollten? Wir müssten alle :loop_amen suchen und mit :loop_garzul ersetzen. Das mag in Ordnung sein, wenn Du viel Zeit hast - aber was, wenn Du gerade auf einer Bühne stehst? Manchmal hast Du nicht den Luxus, Zeit zu haben - vor allem dann nicht, wenn Du willst, dass die Leute weiter tanzen.

Was wäre, wenn Du den Code so geschrieben hättest:

sample_name = :loop_amen
sample sample_name
sleep sample_duration(sample_name)
sample sample_name, rate: 0.5
sleep sample_duration(sample_name, rate: 0.5)
sample sample_name
sleep sample_duration(sample_name)

Das tut genau dasselbe wie der Code weiter oben (probier es aus). Außerdem bekommen wir die Möglichkeit, durch die Änderung der einen Zeile sample_name = :loop_amen in sample_name = :loop_garzul die vielen anderen Stellen durch die Magie der Variablen zu verändern.

Ergebnisse von Dingen speichern

Wenn man schließlich die Ergebnisse von irgendwelchen Dingen speichern möchte, dann ist das ebenfalls ein guter Grund dafür, Variablen zu verwenden. Du möchtest vielleicht irgendetwas mit der Dauer eines Sample anstellen:

sd = sample_duration(:loop_amen)

Wir können nun sd überall da einsetzen, wo wir die Länge von :loop_amen brauchen.

Noch wichtiger vielleicht: Eine Variable erlaubt es uns, das Ergebnis eines Aufrufs von play oder sample zu speichern:

s = play 50, release: 8

Jetzt haben wir s als Variable eingefangen und gemerkt. Und das erlaubt es uns, einen Synth zu steuern, während er läuft:

s = play 50, release: 8
sleep 2
control s, note: 62

Das nur als kleine Vorschau, wie man Synths steuert. Das schauen wir uns später noch genauer an.

Warnung: Variablen und Threads (Prozessreihenfolge)

Während Variablen sehr gut dazu geeignet sind, Dingen Namen zu geben oder ein Ergebnis festzuhalten, so sollten sie aber typischerweise nur im lokalen Kontext eines Threads verwendet werden. Das Folgende sollte man daher nicht tun:

a = (ring 6, 5, 4, 3, 2, 1)
live_loop :sorted do
  a = a.sort
  sleep 0.5
  puts "sorted: ", a
end
live_loop :shuffled do
  a = a.shuffle
  sleep 0.5
end

In dem Beispiel oben weisen wir einem “Ring” aus Zahlen einer Variable ‘a’ zu und verwenden sie dann in zwei separaten live_loops. Innerhalb der ersten live-loop sortieren wir alle 5 Sekunden den Ring (to (ring 1, 2, 3, 4, 5, 6)) und geben in es dann im log aus. Wenn du nun den Code startest, wirst du bemerken, dass die ausgegebene Liste nicht immer sortiert ist!. Das könnte dich verwundern - insbesondere, da manchmal die Liste sortiert ist und manchmal eben nicht. Das bezeichnet man als nicht-deterministisches Verhalten (nicht vorhersagbar) und ist das Ergebnis eines eher üblen Problems, das man als ‘Race-Condition’ bezeichnet. Das Problem resultiert aus der Tatsache, dass die zweite live-loop parallel zur ersten live-loop die Liste verändert (sie mischt sie durch) und zum Zeitpunkt der Ausgabe die Liste manchmal sortiert und manchmal nicht sortiert ist. Da beide live-loops im Wettlauf miteinander sind, die Variable zu verändern, ‘gewinnt’ eben manchmal die eine live-loop oder manchmal die andere.

Hierzu gibt es zwei Lösungen: Verwende nie die gleiche Variable in verschiedenen live-loops oder threads. Beispielsweise gibt der folgende Code die Liste immer richtig sortiert aus, da jede live-loop ihre eigene Variable verwendet:

live_loop :shuffled do
  a = (ring 6, 5, 4, 3, 2, 1)
  a = a.shuffle
  sleep 0.5
end
live_loop :sorted do
  a = (ring 6, 5, 4, 3, 2, 1)
  a = a.sort
  sleep 0.5
  puts "sorted: ", a
end

Allerdings möchte man manchmal Werte zwischen verschiedene Threads teilen: zum Beispiel die aktuelle Tonart, das aktuelle oder den aktuellen Synth. In einem solchen Fall verwendet man in Sonic Pi spezielle System-Kommandos mit Hilfe der Funktionen get und `set, die wir später in Kapitel besprechen.


5.7 - Synchronisation von Threads

Bald wirst Du ausreichend vertraut damit sein, live mit einer Anzahl von gleichzeitig ablaufenden Funktionen und Threads zu coden. Dann wirst Du bemerken, dass es ziemlich leicht ist, einen einzelnen Thread mit einem Fehler zum Abbruch zu bringen. Das ist nicht weiter schlimm, da Du den Thread mit einem Klick auf Ausführen ja einfach neu starten kannst. Wenn Du den Thread aber neu startest, dann läuft er nicht mehr zusammen mit den anderen so wie vorher, er ist nun aus dem Takt mit den ursprünglichen Threads.

Vererbte Zeit

Wir haben zuvor erwähnt, dass neue Threads beim Start mit in_thread alle ihre Einstellungen vom Eltern-Thread erben. Das schließt auch die aktuelle Zeit mit ein. Das bedeutet, dass Threads bei gleichzeitigem Start immer miteinander im Takt sind.

Wenn Du aber einen Thread alleine startest, spielt er in seinem eigenen Takt, der höchstwahrscheinlich nicht mit irgendeinem der anderen laufenden Threads zusammenpasst.

Cue und Sync

Sonic Pi hat mit den Funktionen cue und sync eine Lösung für dieses Problem.

cue erlaubt es uns, regelmäßige Signale an alle anderen Threads mit einem Taktgeber zu versenden. Normalerweise sind die anderen Threads an solchen Signale nicht interessiert und werden sie ignorieren. Mit der sync-Funktion kann man jedoch erreichen, dass ein anderer Thread auf diesen Taktgeber hört.

Eine wichtige Sache, derer man sich bewusst sein sollte: sync ist so ähnlich sleep, weil es den aktuellen Thread für eine bestimmte Zeit anhält und dieser so lange nichts tut. Allerdings sagt man mit sleep, wie lange man warten möchte, während man bei sync nicht weiß, wie lange gewartet wird - denn sync wartet auf das nächste cue eines anderen Threads; das kann kürzer oder länger dauern.

Sehen wir uns das etwas genauer an:

in_thread do
  loop do
    cue :tick
    sleep 1
  end
end
in_thread do
  loop do
    sync :tick
    sample :drum_heavy_kick
  end
end

Hier haben wir zwei Threads - einer davon arbeitet wie ein Metronom; er spielt nichts, aber sendet bei jedem Schlag ein :tick-Taktgebersignal. Der zweite Thread synchronisiert sich mit den tick-Signalen, und wenn er eins davon erhält, übernimmt er die Zeit vom cue-Thread und läuft weiter.

Als Ergebnis hören wir den :drum_heavy_kick Sample genau dann, wenn der andere Thread ein :tick-Signal sendet, auch dann, wenn beide Threads gar nicht zur selben Zeit gestartet sind:

in_thread do
  loop do
    cue :tick
    sleep 1
  end
end
sleep(0.3)
in_thread do
  loop do
    sync :tick
    sample :drum_heavy_kick
  end
end

Dieser freche (zweite) Aufruf von sleep würde eigentlich den zweiten Thread gegenüber dem ersten aus dem Takt bringen. Weil wir aber cue und sync verwenden, synchronisieren wir beide Threads automatisch und umgehen dabei, dass beide in der Zeit auseinanderfallen.

Cue benennen

Du kannst cue-Signale benennen, wie Du willst - nicht nur mit :tick. Du musst nur sicherstellen, dass irgendein anderer Thread sich mit eben diesem Namen synchronisiert - ansonsten wartet der zweite Thread endlos (oder jedenfalls bis Du den Stopp-Button klickst).

Lasst uns ein paar Namen für cue ausprobieren:

in_thread do
  loop do 
    cue [:foo, :bar, :baz].choose
    sleep 0.5
  end
end
in_thread do
  loop do 
    sync :foo 
    sample :elec_beep
  end
end
in_thread do
  loop do
    sync :bar
    sample :elec_flip
  end
end
in_thread do
  loop do
    sync :baz
    sample :elec_blup
  end
end

Hier haben wir eine cue-Hauptschleife, die auf Zufallsbasis einen von drei Namen des Taktgebers :foo, :bar oder :baz aussendet. Wir haben auch drei Schleifen-Threads, die jeder für sich mit einem der Namen synchronisiert werden und dann jeweils ein anderes Sample spielen. Im Endeffekt hören wir bei jedem halben Schlag einen Klang, da jeder der sync-Threads auf Zufallsbasis mit dem cue-Thread synchronisiert ist, und sein Sample spielt, wenn er dran ist.

Das funktioniert natürlich auch, wenn Du die Reihenfolge der Threads umkehrst: Die sync-Threads sitzen einfach da und warten auf ihren nächsten cue.


6 - Studio-Klangeffekte

Es lohnt sich, mit Sonic Pi Deine Klänge mit Studio-Effekten zu verändern - Du wirst sehen, wie spannend das ist und wieviel Spaß das macht. Zum Beispiel kannst Du zu Teilen Deiner Komposition Hall-Effekte (“Reverb”) hinzufügen. Oder etwas Echo (“Echo”). Oder vielleicht möchtest Du Deine Bassmelodien verzerren (“Distortion”) oder schwabbeln lassen (“Wobble”).

Mit Sonic Pi kannst Du sehr mächtige Klangeffekte auf ganz einfache Art einfügen. Du kannst sie sogar miteinander verketten oder sie ineinander verschachteln (zum Beispiel könntest Du einen Ton zuerst verzerren, dann Echo hinzufügen und zum Schluss noch etwas Hall). Jeder einzelne Effekt lässt sich mit Parametern steuern (ganz ähnlich wie auch Synths und Samples). Du kannst sogar die Parameter eines Klangeffektes ändern, während der Effekt läuft. Zum Beispiel könntest Du den Halleffekt Deiner Bassmelodie während des Stückes langsam anheben…

Gitarreneffekte

Wenn sich das alles etwas kompliziert anhören sollte, keine Sorge! Spiel ein bisschen mit den Effekten herum und es wird Dir ganz schnell alles viel einfacher vorkommen. Bevor Du loslegst, könnte dieser kleine Vergleich nützlich sein: Die Effekte in Sonic Pi funktionieren so ähnlich wie Gitarreneffektpedale. Es gibt eine Vielzahl von solchen Effektpedalen zu kaufen. Manche Gitarristen benutzen gerne Hall-Effekte (“Reverb”), andere bevorzugen ein Verzerrerpedal (“Distortion”), etc. Als Gitarrist kann man einfach seine Gitarre an ein Effektpedal anstecken - z.B. “Distortion” - und dann mit einem weiteren Kabel noch ein Hallpedal anhängen. So entsteht eine Kette von Effekten. Das Ausgangssignal des Hall-Effektpedals am Ende der Kette kann man dann in einen Verstärker leiten:

Gitarre -> Verzerrer -> Hall -> Verstärker

Dies nennt man “Effektverkettung”. Und genau das kannst Du mit Sonic Pi machen. Jedes Effektpedal hat normalerweise einige Drehknöpfe und Schalter, mit denen man exakt steuern kann, wieviel Verzerrung, Hall, oder Echo erzeugt werden soll. Und eines noch zum Schluss: Du kannst Dir sicher vorstellen, dass ein Gitarrist mit Gitarrespielen beschäftigt sein könnte, während jemand anderes die Effekte steuert. Das ist auch bei Sonic Pi möglich - aber anstatt auf jemand anderen dafür angewiesen zu sein, steht für Dich Dein Computer am Effektmischer und hilft Dir.

Aber jetzt ist es höchste Zeit, die Effektfunktionen auszuprobieren!


6.1 - FX hinzufügen

In diesem Abschnitt sehen wir uns zwei Effekte genauer an: Hall und Echo. Wir werden lernen, wie diese Effekte zu benutzen sind, wie man ihre Parameter steuern kann, und wie man sie verketten kann.

Das FX-System von Sonic Pi benutzt sogenannte Code-Blocks. Falls Du Abschnitt 5.1 des Tutorials noch nicht gelesen hast, sieh es Dir doch kurz an und kehre dann hierher zurück.

Hall (“Reverb”)

Um den Hall-Effekt zu benutzen, fügen wir with_fx :reverb als Spezialcode zu unserem bestehenden Code-Block hinzu. Und zwar so:

with_fx :reverb do
  play 50
  sleep 0.5
  sample :elec_plip
  sleep 0.5
  play 62
end

Wenn Du nun diesen Code-Block abspielst, wirst Du ihn mit Hall hören. Hört sich gut an, nicht wahr! Mit Hall-Effekt hört sich alles immer ziemlich gut an.

Aber was passiert, wenn wir den Code außerhalb des do/end-Code-Blocks hinzufügen:

with_fx :reverb do
  play 50
  sleep 0.5
  sample :elec_plip
  sleep 0.5
  play 62
end
sleep 1
play 55

Wie Du sicher bemerkt hast, wird nun die letzte Note (55) nicht mehr mit Hall-Effekt abgespielt. Der Grund dafür ist, dass sich die Note außerhalb des do/end Code-Blocks befindet und deshalb unsere Anweisung zum Hall-Effekt für sie nicht mehr gültig ist.

Wenn Du Noten vor dem do/end-Code-Block einfügst, verhält es sich ganz ähnlich, und sie werden ebenfalls ohne Hall-Effekt abgespielt:

play 55
sleep 1
with_fx :reverb do
  play 50
  sleep 0.5
  sample :elec_plip
  sleep 0.5
  play 62
end
sleep 1
play 55

Echo

Es stehen noch viele weitere Effekte zur Auswahl. Wie wäre es zum Beispiel mit etwas Echo?

with_fx :echo do
  play 50
  sleep 0.5
  sample :elec_plip
  sleep 0.5
  play 62
end

Die FX-Blocks von Sonic Pi sind besonders vielfältig einsetzbar, denn sie lassen sich über Parameter steuern. Genauso wie die Befehle play und sample, die wir ja schon kennen. Ein spannender Echo-Parameter ist zum Beispiel phase:. Dieser Parameter stellt die Dauer des Echos in Schlägen dar. Versuchen wir, das Echo etwas langsamer zu machen:

with_fx :echo, phase: 0.5 do
  play 50
  sleep 0.5
  sample :elec_plip
  sleep 0.5
  play 62
end

Und jetzt lass uns das Echo wieder schneller machen:

with_fx :echo, phase: 0.125 do
  play 50
  sleep 0.5
  sample :elec_plip
  sleep 0.5
  play 62
end

Wie wäre es nun mit einem langsam ausschwingenden Echo? Wir können dafür einfach den decay:-Wert auf 8 Schläge verlängern:

with_fx :echo, phase: 0.5, decay: 8 do
  play 50
  sleep 0.5
  sample :elec_plip
  sleep 0.5
  play 62
end

FX verschachteln

Interessant wird es, wenn wir die FX-Blocks ineinander verschachteln. So kannst Du ganz unterschiedliche FX leicht verketten. Wie wäre es zum Beispiel damit, einen Code-Block zuerst mit Echo und dann mit Hall zu versehen? Das geht ganz einfach, Du musst nur die beiden Code-Blocks ineinander verschachteln:

with_fx :reverb do
  with_fx :echo, phase: 0.5, decay: 8 do
    play 50
    sleep 0.5
    sample :elec_blup
    sleep 0.5
    play 62
  end
end

Stell Dir das so vor, als ob die erzeugten Klänge von innen nach außen fließen würden. Der erzeugte Klang des innersten do/end-Code-Blocks (zum Beispiel play 50) wird zuerst durch den Echo-FX-Block gesendet, und danach noch durch den Hall-FX-Block.

Mit vielschichtigen Verschachtelungen kannst Du verrückte Ergebnisse ganz einfach erzielen. Aber sei vorsichtig, weil FX viel Rechenkraft des Computers verbraucht, besonders dann, wenn Du mehrere FX zugleich benutzt. Sei also etwas sparsam mit Deinen FX, wenn Du mit eher rechenschwachen Geräten wie dem Raspberry Pi arbeitest.

Weitere FX entdecken

Sonic Pi bringt eine Vielzahl von FX mit, die Du ausprobieren kannst. Eine komplette Liste findest Du im Hilfe-Fenster, wenn Du dort links auf ‘FX’ klickst. Hier sind einige meiner Favoriten:

wobble, reverb, echo, distortion, slicer

Nun drehe so richtig auf und füge überall in deinem Code Audioeffekte hinzu. Du wirst fantastische Sounds erschaffen!


6.2 - Anwenden von Soundeffekten

Auf den ersten Blick mögen die Effekte von Sonic Pi sehr einfach aussehen, aber in Wirklichkeit sind sie höchst komplexe Gebilde. Ihre scheinbare Einfachheit verleitet manche dazu, zu viele FX in ihre Projekte einzubauen. Das kann durchaus Spaß machen, wenn Du über einen sehr starken Computer hast. Aber falls Du - so wie ich - einen einfachen Raspberry Pi zum Musikmachen benutzt, musst Du darauf achten, die Maschine nicht zu überlasten. Nur so kannst Du sicher gehen, dass Sonic Pi ordentlich funktioniert und im Takt bleibt.

Betrachte den folgenden Code:

loop do
  with_fx :reverb do
    play 60, release: 0.1
    sleep 0.125
  end
end

Hier spielen wir die Note 60 mit sehr kurzem Ausklang (“release”), und erzeugen somit einen kurzen Ton. Wir wollen außerdem einen Hall-Effekt, also haben wir den nötigen “reverb”-Block darum herum geschrieben. Bisher ist alles gut, abgesehen von…

Sehen wir uns genauer an, was der Code hier macht. Zuerst einmal haben wir einen Loop, und wie wir ja schon wissen wird alles, was innerhalb der Endlosschleife geschrieben ist, für immer und ewig wiederholt. Außerdem haben wir einen with_fx:-do/end-Block. Das bedeutet, dass bei jeder Wiederholung des Loops ein neuer Hall-Effekt erzeugt wird. Das ist so, als ob wir jedes Mal, wenn wir eine Gitarrenseite zupfen, ein neues Pedal an die Effektkette hängen würden. Es ist ja ganz nett, das wir das machen können, aber es ist möglicherweise nicht das, was wir eigentlich wollen. Ein kleiner Raspberry Pi wird an diesem Code schwer zu arbeiten haben. Die with_fx:-Funktion erledigt die ganze Arbeit, bei jeder Wiederholung des Loops den Hall-Effekt zu erzeugen, dann zu warten, und dann den Effekt wieder zu entfernen, aber insgesamt werden dabei wertvolle CPU Ressourcen verbraucht.

Wie können wir einen ähnlichen Code-Block schreiben, bei dem unser Gitarrist nur ein einzelnes Hall-Effektpedal zur Klangerzeugung benutzt? Ganz einfach:

with_fx :reverb do
  loop do
    play 60, release: 0.1
    sleep 0.125
  end
end

Nun haben wir unseren Loop ins Innere des with_fx:-Blocks gesetzt. So wird nur noch ein einziger Hall-Effekt erzeugt und auf alle Noten im Loop angewandt. Dieser Code-Block ist viel effizienter und wird auf jedem Raspberry Pi gut funktionieren.

Als Kompromiss könnten wir with_fx: auch als Iteration innerhalb eines Loops schreiben:

loop do
  with_fx :reverb do
    16.times do
      play 60, release: 0.1
      sleep 0.125
    end
  end
end

Somit wird die with_fx:-Funktion aus dem Inneren des Loops geholt, und der Code erzeugt jetzt nur noch einen Hall-Effekt alle 16 Noten.

Dieser Ausdruck taucht so häufig auf, dass with_fx dafür eine Option bereitstellt; Du musst also nicht den Block 16.times hinschreiben:

loop do
  with_fx :reverb, reps: 16 do
    play 60, release: 0.1
    sleep 0.125
  end
end

Beide, reps: 16 und 16.times do verhalten sich indentisch. reps: 16 wiederholt den Code im do/end-Block 16-mal, sodass Du beide Ausdrücke untereinander austauschen kannst; nimm’ einfach den, der Dir am besten gefällt.

Denk daran, man kann nichts falsch machen, dafür aber unendlich viel ausprobieren! Jedes Mal wird es sich ein bisschen anders anhören, und jeweils mehr oder weniger gut funktionieren. Experimentiere also so viel wie möglich, bis Du gefunden hast, was am besten funktioniert und den Einschränkungen Deines Computers am besten gerecht wird.


7 - Laufende Sounds steuern

Bisher haben wir uns angeschaut, wie man Synths und Samples starten und ihre Standard-Einstellungen wie Lautstärke, Position im Stereofeld, Hüllkurven und so weiter anpassen kann. Jeder abgespielte Ton ist im Grunde genommen ein eigener Klang mit seinen eigenen Optionen für seine Abspieldauer.

Wäre es nicht cool, wenn man diese Opts verändern könnte - während der Ton noch abgespielt wird? Etwa so, wie Du beim Gitarrespielen an einer noch schwingende Saite ziehen kannst?

Glück gehabt - dieses Kapitel wird Dir genau das zeigen.


7.1 - Laufende Synths steuern

Bisher haben wir uns nur damit befasst, neue Sounds und Effekte zu starten. Mit Sonic Pi kannst Du aber auch laufende Klänge steuern und verändern. Um das zu tun, speichern wir die Referenz zu einem Synth in einer Variable:

s = play 60, release: 5

Jetzt haben wir eine run-local Variable s. Sie repräsentiert einen Synth, der die Note 60 spielt. Beachte, dass diese Variable eine lokale ist - Du kannst nicht von anderen Runs, wie beispielsweise Funktionen, auf sie zugreifen.

Sobald wir die Referenz in der Variable s haben, können wir den Synth mit Hilfe der control-Funktion steuern:

s = play 60, release: 5
sleep 0.5
control s, note: 65
sleep 0.5
control s, note: 67
sleep 3
control s, note: 72

Wichtig ist hier, dass wir nicht vier verschiedene Synths starten - wir starten einen einzigen und ändern danach dreimal die Tonhöhe, während er noch spielt.

Wir können jeden der Standard-Parameter an die control-Funktion übergeben. Das erlaubt Dir, Dinge wie amp:, cutoff: oder pan: zu kontrollieren.

Nicht-kontrollierbare Parameter

Manche Opts lassen sich nach dem Start des Synths nicht mehr verändern. Das trifft auf alle ADSR-Hüllkurven-Parameter zu. Welche Opts kontrollierbar sind, kannst Du in der Dokumentation im Hilfe-System herausfinden. Parameter, die mit Can not be changed once set markiert sind, lassen sich nach dem Synth-Start nicht mehr verändern.


7.2 - Effekte steuern

Man kann auch Effekte steuern, allerdings geht das ein wenig anders:

with_fx :reverb do |r|
  play 50
  sleep 0.5
  control r, mix: 0.7
  play 55
  sleep 1
  control r, mix: 0.9
  sleep 1
  play 62
end

Statt eine Variable zu verwenden, nutzen wir die senkrechten Striche des do/end-Blocks. Innerhalb der Striche |, müssen wir einen eindeutigen Namen für unseren laufenden Effekt vergeben, den wir dann innerhalb des do/end-Blocks verwenden. Dieses Verhalten ist das gleiche wie bei parametrisierten Funktionen.

Jetzt los - steuere ein paar Synths und Effekte!


7.3 - Gleitende Optionen

Vielleicht ist Dir beim Erforschen der Synth- und FX-Opts aufgefallen, dass manche davon mit _slide enden. Vielleicht hast Du sie sogar ausprobiert und keine Auswirkung bemerkt. Das liegt daran, dass dies keine normalen Opts sind. Diese besonderen Opts funktionieren nur dann, wenn Du Synths wie im letzten Kapitel beschrieben steuerst.

Schau Dir das folgende Beispiel an:

s = play 60, release: 5
sleep 0.5
control s, note: 65
sleep 0.5
control s, note: 67
sleep 3
control s, note: 72

Hier kannst Du hören, wie sich die Tonhöhe unmittelbar bei jedem Aufruf von control ändert. Vielleicht möchtest Du aber, dass die Tonhöhe sich gleitend zwischen den Aufrufen verändert. Um dort, wo wir die Opt note: verwenden, gleitende Übergänge hinzuzufügen, nutzen wir die note_slide:-Opt des Synths:

s = play 60, release: 5, note_slide: 1
sleep 0.5
control s, note: 65
sleep 0.5
control s, note: 67
sleep 3
control s, note: 72

Jetzt hören wir, wie die Noten zwischen den control-Aufrufen “gezogen” werden. Hört sich gut an, oder? Mit einer kürzeren Dauer wie beispielsweise note_slide: 0,2 kannst Du den Übergang beschleunigen. Oder ihn mit einer längeren Slide-Dauer verlangsamen.

Jeder steuerbare Parameter hat einen entsprechenden _slide-Parameter, mit dem Du spielen kannst.

Gleiten ist klebrig

Nachdem Du einmal einen _slide Parameter auf einem laufenden Synth angegeben hast, bleibt er bestehen und wird jedesmal genutzt, wenn Du den entsprechenden Parameter steuerst. Um das Gleiten auszuschalten, musst Du den _slide Wert vor dem nächsten control-Aufruf auf 0 setzen.

Gleitende Effekt-Parameter

Genauso ist es möglich, Effekt-Opts gleiten zu lassen:

with_fx :wobble, phase: 1, phase_slide: 5 do |e|
  use_synth :dsaw
  play 50, release: 5
  control e, phase: 0.025
end

Und jetzt lass die Dinge ein wenig gleiten. Viel Spaß mit den weichen Übergängen…


8 - Datenstrukturen

Ein sehr praktisches Werkzeug im Werkzeugkasten jedes Programmierers sind Datenstrukturen.

Manchmal möchtest Du mehr als eine Sache darstellen oder verwenden. Es könnte beispielsweise nützlich sein, sich eine Reihe von Noten zu merken und sie dann nacheinander abzuspielen. In Programmiersprachen sind Datenstrukturen genau für diesen Zweck da.

Es sind viele aufregende und exotische Datenstrukturen für Programmierer verfügbar - und es werden immer noch neue erfunden. Für unsere Zwecke benötigen wir vorerst jedoch nur eine sehr einfache Datenstruktur: Die Liste.

Lass uns das gleich im Detail anschauen. Wir werden uns die Grundform einer Liste anschauen und wie man sie verwendet, um Skalen und Akkorde abzubilden.


8.1 - Listen

In diesem Abschnitt werden wir uns eine sehr nützliche Datenstruktur anschauen - die Liste. Wir hatten schon einmal in dem Abschnitt über Randomisierung kurz mit ihr zu tun, als wir die zu spielenden Noten zufällig aus einer Liste auswählten:

play choose([50, 55, 62])

In diesem Abschnitt erforschen wir, wie man Akkorde und Skalen mit Listen darstellen kann. Rufen wir uns wieder in Erinnerung, wie wir einen Akkord spielen könnten. Ohne sleep werden alle Töne zur gleichen Zeit abgespielt:

play 52
play 55
play 59

Wir können diesen Code aber auch anders schreiben.

Eine Liste abspielen

Eine Möglichkeit ist es, alle Noten aus einer Liste zu spielen: [52, 55, 59]. Unsere freundliche Funktion play ist schlau genug, um zu wissen, wie man eine Liste von Noten abspielt. Versuche es einmal:

play [52, 55, 59]

Prima, das kann man schon viel besser lesen. Das Abspielen einer Liste hindert uns aber nicht daran, die gewohnten Parameter zu verwenden:

play [52, 55, 59], amp: 0.3

Natürlich kannst Du auch die traditionellen Namen der Noten anstelle der MIDI-Nummern verwenden:

play [:E3, :G3, :B3]

Die Glücklichen unter Euch, die ein bisschen Musiktheorie gelernt haben, erkannten den Akkord vielleicht: Es ist E-Moll in der dritten Oktave.

Listenzugriffe

Sehr nützlich an Listen ist, dass man auf die enthaltenen Informationen zugreifen kann. Das klingt vielleicht seltsam, aber es ist nicht komplizierter als beispielsweise das Öffnen eines Buches auf der Seite 23. Eine Liste fragt man einfach “Was ist das Element am Index 23?”. Du musst Dir nur eine Merkwürdigkeit merken: Beim Programmieren beginnt man beim Zählen der Listenelemente normalerweise mit 0 statt mit 1.

Listenindizes zählen wir also nicht 1, 2, 3… sondern 0, 1 ,2…

Wir schauen uns das etwas genauer an. Nehmen wir diese Liste:

[52, 55, 59]

Nicht besonders beängstigend. Was ist das zweite Element dieser Liste? Natürlich, das ist 55. Das war einfach. Jetzt versuchen wir, ob wir nicht den Computer dazu bringen können, die Frage für uns zu beantworten:

puts [52, 55, 59][1]

Falls Du so etwas noch nie gesehen hast, mag das ein bisschen seltsam wirken. Aber vertrau mir, das ist nicht schwierig zu durchschauen. Die Zeile oben besteht aus drei Teilen: Dem Wort puts, unserer Liste 52, 55, 59 und unserem Index [1]. Zunächst sagen wir puts, damit Sonic Pi die Antwort für uns ins Protokoll schreibt. Danach übergeben wir unsere Liste und am Ende den Index, mit dem wir nach dem zweiten Element der Liste fragen. Den Index müssen wir mit eckigen Klammern umschließen, und da wir bei 0 zu zählen beginnen, ist der Index des zweiten Elements 1. Wie hier unten:

# indexes:  0   1   2
           [52, 55, 59]

Versuche mal, den Code puts [52, 55, 59][1] auszuführen, dann wirst Du sehen, dass 55 im Protokoll erscheint. Ändere den Index 1 in einen anderen, probiere längere Listen aus und denke darüber nach, wie Du Listen in Deinem nächsten Code-Jam verwenden könntest. Überlege dir zum Beispiel, welche Strukturen aus der Musik man als eine Reihe von Zahlen interpretieren könnte…


8.2 - Akkorde

Sonic Pi kann Dir den Namen eines Akkords in eine Liste seiner Töne übersetzen. Versuche es selbst:

play chord(:E3, :minor)

Jetzt tut sich was. Das sieht doch schon viel schöner aus als einfache Listen (und ist für andere viel einfacher lesbar). Welche Akkorde kennt Sonic Pi noch? Naja, viele. Probier mal ein paar von diesen aus:

chord(:E3, :m7) chord(:E3, :minor) chord(:E3, :dim7) chord(:E3, :dom7)

Arpeggios

Um Akkorde einfach in Arpeggios umzuwandeln, können wir die Funktion play_pattern verwenden:

play_pattern chord(:E3, :m7)

Ok, das war nichts besonderes - das ging wirklich langsam. play_pattern spielt jede Note in der Liste hintereinander, getrennt durch Aufrufe von sleep 1 ab. Wir können die Funktion play_pattern_timed verwenden, um unsere eigenen Zeitdauern zu verwenden und das Ganze zu beschleunigen:

play_pattern_timed chord(:E3, :m7), 0.25

Wir können sogar eine Liste von Dauern übergeben; Sonic Pi verwendet diese nacheinander und wiederholt sie solange wie notwendig:

play_pattern_timed chord(:E3, :m13), [0.25, 0.5]

Das bedeutet das gleiche wie:

play 52
sleep 0.25
play 55
sleep 0.5
play 59
sleep 0.25
play 62
sleep 0.5
play 66
sleep 0.25
play 69
sleep 0.5
play 73

Was würdest Du lieber schreiben?


8.3 - Skalen

Sonic Pi kennt alle möglichen Tonleitern. Wie wäre es mit einer C3-Dur-Skala?

play_pattern_timed scale(:c3, :major), 0.125, release: 0.1

Wir können die Skala auch über mehrere Oktaven spielen lassen:

play_pattern_timed scale(:c3, :major, num_octaves: 3), 0.125, release: 0.1

Und wie wär’s mit allen Noten einer pentatonischen Tonleiter?

play_pattern_timed scale(:c3, :major_pentatonic, num_octaves: 3), 0.125, release: 0.1

Zufällige Noten

Akkorde und Skalen sind gute Methoden, eine zufällige Auswahl auf etwas von Bedeutung einzugrenzen. Spiele einmal mit diesem Beispiel, das zufällige Noten aus dem E-Moll-Akkord in der 3. Oktave auswählt:

use_synth :tb303
loop do
  play choose(chord(:E3, :minor)), release: 0.3, cutoff: rrand(60, 120)
  sleep 0.25
end

Probiere auch verschiedene Akkord-Namen und Cut-Off-Bereiche aus.

Akkorde und Skalen entdecken

Um herauszufinden, welche Skalen und Akkorde Sonic Pi unterstützt, klicke einfach auf den Lang-Button ganz auf der linke Seite dieses Tutorials und wähle entweder chord oder scale aus der API-Liste. Scrolle im Hauptfenster herunter, bist Du eine lange Liste von Akkorden oder Skalen findest.

Viel Spaß und nicht vergessen: Es gibt keine Fehler, nur Möglichkeiten.


8.4 - Ringe

Eine interessante Spielart von normalen Listen sind Ringe. Wenn Du ein bisschen Programmiererfahrung hast, könntest Du Ringspeicher (engl. „Ring Buffer“) oder Ring-Arrays kennen. Wir nennen die hier nur Ring – das ist kurz und einfach.

Im vorherigen Abschnitt über Listen haben wir gesehen, dass wir Elemente aus einer Liste über den Index holen können:

puts [52, 55, 59][1]

Was passiert jetzt wohl, wenn Du den Index 100 abfragen willst? Offensichtlich gibt es kein Element mit dem Index 100, da die Liste nur drei Elemente enthält. Also gibt Sonic Pi nil zurück, was “nichts” bedeutet.

Stell Dir vor, einen Zähler wie den aktuellen Takt zu haben, der kontinuierlich wächst. Lass uns so einen Zähler und unsere Liste anlegen:

counter = 0
notes = [52, 55, 59]

Jetzt können wir mit unserem Zähler auf eine Note in unserer Liste zugreifen:

puts notes[counter]

Super, da kam 52 heraus. Jetzt erhöhen wir den Zähler (inc, die Abkürzung für englisch “to increment” = erhöhen) und bekommen damit eine neue Note:

counter = (inc counter)
puts notes[counter]

Ok, jetzt kommen 55 und beim nächsten Mal 59 heraus. Wenn wir das jedoch jetzt noch einmal machen, werden wir aber nicht mehr genug Zahlen in unserer Liste haben und werden stattdessen nil zurück bekommen. Was wäre, wenn wir dann einfach wieder vom Anfang der Liste anfangen wollten? Genau dafür gibt es Ringe.

Ringe erzeugen

Es gibt zwei Möglichkeiten, Ringe zu erzeugen. Die erste ist, die ring Funktion mit den gewünschten Elementen des Rings als Parameter zu verwenden:

(ring 52, 55, 59)

Die zweite Möglichkeit ist es, eine normale Liste mit der Nachricht .ring zu einem Ring umzuwandeln:

[52, 55, 59].ring

Auf Ringelemente zugreifen

Sobald wir einen Ring haben, können wir ihn auf die gleiche Art wie eine Liste verwenden. Die einzige Ausnahme ist, dass Du Indizes verwenden kannst, die negativ oder größer als der Ringinhalt sind. Diese fangen dann wieder am Anfang an, um immer auf ein Element des Rings zu zeigen:

(ring 52, 55, 59)[0] #=> 52
(ring 52, 55, 59)[1] #=> 55
(ring 52, 55, 59)[2] #=> 59
(ring 52, 55, 59)[3] #=> 52
(ring 52, 55, 59)[-1] #=> 59

Ringe verwenden

Angenommen, wir bilden die Nummer des aktuellen Takts in einer Variable ab. Diese Nummer können wir als Zugriffsindex für unseren Ring verwenden, um abzuspielende Note, Release-Zeiten oder andere sinnvolle Dinge, die wir in unserem Ring abgelegt haben, abzurufen.

Skalen und Akkorde sind Ringe

Gut zu wissen: Die Listen, die von scale und chord zurückgegeben werden, sind Ringe. Du kannst also mit beliebigen Indizes auf sie zuzugreifen.

Ring-Konstruktoren

Zusätzlich zu ring gibt es noch eine Anzahl weiterer Funktionen, die Ringe für uns erzeugen.

range lädt ein, einen Startpunkt, einen Endpunkt und eine Schrittgröße zu definieren. bools erlaubt Dir 1er und 0er als prägnante Darstellung boolscher Werte zu nutzen. knit erlaubt Dir, eine Abfolge wiederholter Werte zu “stricken” (engl. “to knit”). spread verteilt boolschen Werte mit dem Euklidischen Algorithmus in einem Ring.

Mehr Informationen zu diesen Funktionen findest Du in der entsprechenden Dokumentation.


8.5 - Ringe mit verketteten Kommandos

Zusätzlich zu den Konstruktoren wie range und spread können neue Ringe auch erzeugt werden, indem schon existierende manipuliert werden.

Verkettete Kommandos

Nimm’ einen einfachen Ring, um das auszuprobieren:

(ring 10, 20, 30, 40, 50)

Was, wenn wir die Reihenfolge umkehren wollen? Dann verwenden wir das verkettete Kommando .reverse, um den Ring umzudrehen:

(ring 10, 20, 30, 40, 50).reverse  #=> (ring 50, 40, 30, 20, 10)

Was nun, wenn wir die ersten 3 Werte aus dem Ring haben möchten?

(ring 10, 20, 30, 40, 50).take(3)  #=> (ring 10, 20, 30)

Und was, wenn wir schließlich den Ring mischen wollten?

(ring 10, 20, 30, 40, 50).shuffle  #=> (ring 40, 30, 10, 50, 20)

Mehrteilige Ketten

So haben wir schon viele Möglichkeiten neue Ringe zu erzeugen. Noch besser wird es, wenn Du diese Kommandos miteinander verkettest.

Wie wäre es z. B. wenn Du den Ring mischst, das erste Element weglässt, und dann die nächsten 3 Elemente nimmst?

Gehen wir das Schritt für Schritt durch:

(ring 10, 20, 30, 40, 50) - Dein Ring am Anfang (ring 10, 20, 30, 40, 50).shuffle - mischt - (ring 40, 30, 10, 50, 20) (ring 10, 20, 30, 40, 50).shuffle.drop(1) - das erste Element weglassen - (ring 30, 10, 50, 20) (ring 10, 20, 30, 40, 50).shuffle.drop(1).take(3) - 3 Elemente nehmen - (ring 30, 10, 50)

Siehst Du, wie wir eine lange Kette dieser Methoden einfach dadurch erzeugen können, dass wir sie aneinander hängen? Wir können das in jeder beliebigen Reihenfolge tun; damit haben wir viele unterschiedliche Möglichkeiten, neue Ringe aus schon bestehenden zu erzeugen.

Unveränderlichkeit

Diese Ringe haben eine leistungsstarke und wichtige Eigenschaft. Sie sind unveränderlich (engl. immutable). Das heisst, dass die Verkettung von Methoden, von der wir hier sprechen, die Ringe nicht verändert sondern neue Ringe erzeugt. Du kannst also Ringe in verschiedenen Threads verwenden und sie mit verketteten Kommandos verändern, ohne dass derselbe Ring in einem anderen Thread verändert wird.

Methoden, die verkettet werden können

Hier kommt eine Liste der Methoden, die miteinander verkettet werden können:

.reverse - gibt eine umgedrehte Version des Rings zurück .sort - stellt eine sortierte Version des Rings her .shuffle - liefert eine neu gemischte Version des Rings .pick(3) - liefert einen Ring nachdem 3-mal .choose angewendet wurde .pick(3) - liefert einen Ring nachdem 3-mal .choose angewendet wurde .take(5) - liefert einen neuen Ring, der nur die ersten 5 Elementen enthält .drop(3) - liefert einen neuen Ring, der alles außer den ersten 3 Elementen enthält .butlast - liefert einen neuen Ring ohne das letzte Element .drop_last(3) - liefert einen neuen Ring ohne die letzten 3 Elemente .take_last(6)- liefert einen neuen Ring, der nur die letzten 6 Elemente enthält .stretch(2) - wiederholt jedes Element im Ring zweimal .repeat(3) - wiederholt den ganzen Ring 3-mal .mirror - hängt eine umgekehrte Version des Rings an die ursprüngliche an .reflect - wie .mirror, verdoppelt aber nicht die mittleren Werte .scale(2) - gibt einen neuen Ring zurück, der alle Elemente des ursprünglichen Rings mit 2 multipliziert enthält (Voraussetzung: Ring enthält nur Zahlen)

Die Verkettungsfunktionen mit Zahlenparametern können natürlich auch mit anderen Werten aufgerufen werden. Anstelle von .drop(3) kann man zum Beispiel .drop(5) aufrufen, um die ersten fünf Elemente wegfallen zulassen.


9 - Live-Coding

So, und jetzt wird es aufregend! Mit Sonic Pi kannst Du den Code für Deine Musik live schreiben und verändern. Du kannst also während eines Auftritts mit Sonic Pi live Musik machen - wie mit einer Gitarre. Das Großartige daran ist, dass Du noch während des Komponierens sofort die Rückmeldung vom Publikum erhältst. Starte einen einfachen Loop und spiele daran herum, bis er genau richtig klingt. Der größte Vorteil ist aber, dass Du Sonic Pi bei einem Gig mit auf die Bühne nehmen kannst.

In diesem Abschnitt behandeln wir die Grundlagen, mit denen Du Deine Kompositionen von statischem Code in rasante Performances verwandeln kannst.

Halt Dich fest…


9.1 - Live-Coding

Inzwischen haben wir genug gelernt, nun können wir wirklich Spaß haben. In diesem Abschnitt nutzen wir alles, was wir bisher in diesem Tutorial gelernt haben und zeigen Dir, wie Du Deine Kompositionen live aufführen und in Performances verwandeln kannst. Dazu benötigen wir vor allem drei Zutaten:

Die Fähigkeit, Code zu schreiben, der Töne erzeugt - CHECK! Die Fähigkeit, Funktionen zu schreiben - CHECK! Die Fähigkeit, (named) Threads zu verwenden - CHECK!

Ok, es kann also los gehen. Lass uns unsere ersten Sounds live coden. Zuerst brauchen wir eine Funktion, die den abzuspielenden Code enthält. Wir fangen ganz einfach an. Außerdem wollen wir diese Funktion in einer Schleife in einem Thread aufrufen:

define :my_loop do
  play 50
  sleep 1
end
in_thread(name: :looper) do
  loop do
    my_loop
  end
end

Wenn Dir das ein bisschen zu kompliziert aussieht, lies bitte noch einmal die Abschnitte über Funktionen und Threads durch. Wenn Du diese Dinge verstanden hast, wird es Dir nicht mehr sehr kompliziert vorkommen.

Wir haben hier eine Funktion, welche die Note 50 spielt und danach eine Schlagzeit lang schläft. Dann definieren wir einen benamten Thread, den wir :looper nennen. Dieser ruft in einer Endlosschleife my_loop auf.

Wenn Du diesen Code ausführst, wirst Du immer und immer wieder die Note 50 hören…

Hochschalten

Jetzt geht es richtig los. Während der Code noch läuft, ändere die 50 in eine andere Zahl, zum Beispiel 55, und drücke den Ausführen-Button erneut. Woah! Es hat sich verändert! Live!

Wir haben keine neue Ebene bekommen, da wir einen benannten Thread verwendet haben und es nur einen Thread mit diesem Namen geben kann. Der Ton hat sich geändert, weil wir die Funktion umgeschrieben haben. Wir haben also :my_loop eine veränderte Funktion verpasst. Der :looper-Thread hat einfach diese veränderte Funktion aufgerufen.

Verändere die Funktion noch einmal, ändere die Note und die Pausenzeit. Wie wäre es damit, ein use_synth-Statement einzufügen? Ändere die Funktion zum Beispiel in folgenden Code:

define :my_loop do
  use_synth :tb303
  play 50, release: 0.3
  sleep 0.25
end

Jetzt klingt es schon ganz interessant, wir können es aber noch ein bisschen aufpeppen. Anstatt immer wieder die gleiche Note zu spielen, versuche es einmal mit einem Akkord:

define :my_loop do
  use_synth :tb303
  play chord(:e3, :minor), release: 0.3
  sleep 0.5
end

Und wie wäre es damit, zufällige Töne des Akkords zu spielen:

define :my_loop do
  use_synth :tb303
  play choose(chord(:e3, :minor)), release: 0.3
  sleep 0.25
end

Oder einen zufälligen Cut-Off-Wert:

define :my_loop do
  use_synth :tb303
  play choose(chord(:e3, :minor)), release: 0.2, cutoff: rrand(60, 130)
  sleep 0.25
end

Und zum Schluss noch ein Schlagzeug dazu:

define :my_loop do
  use_synth :tb303
  sample :drum_bass_hard, rate: rrand(0.5, 2)
  play choose(chord(:e3, :minor)), release: 0.2, cutoff: rrand(60, 130)
  sleep 0.25
end

So langsam wird das hier alles viel spannender!

Bevor Du jetzt aufspringst und mit Funktionen und Threads live codest, solltest Du lieber erst einmal eine Pause machen und das nächste Kapitel über den live_loop lesen. Denn dieser wird für immer verändern, wie Du in Sonic Pi programmierst…


9.2 - Live-Loops

Ok, dieses Kapitel des Tutorials ist ein Juwel. Wenn Du nur einen Abschnitt lesen würdest, sollte es dieser sein. Mit live_loop kannst Du genau das, was Du im letzten Abschnitt gelernt hast, viel einfacher und mit weniger Code tun.

Falls Du den letzten Abschnitt nicht gelesen hast: live_loop ist der beste Weg, mit Sonic Pi zu jammen.

Also los. Schreibe Folgendes in einen neuen Puffer:

live_loop :foo do
  play 60
  sleep 1
end

Jetzt klicke den Ausführen-Button. Du hörst zu jeder Schlagzeit ein einfaches Piepen. Kein besonderer Spaß. Drücke aber noch nicht den Stopp-Button. Ändere die 60 in 65 und drücke wieder Ausführen.

Whoa! Es hat sich automatisch verändert, ohne einen Takt auszulassen. Das ist Live-Coding.

Warum es jetzt nicht ein wenig bassiger machen? Verändere Deinen Code, während er abläuft:

live_loop :foo do
  use_synth :prophet
  play :e1, release: 8
  sleep 8
end

Dann drücke Ausführen.

Lass uns den Cut-Off ein wenig bewegen:

live_loop :foo do
  use_synth :prophet
  play :e1, release: 8, cutoff: rrand(70, 130)
  sleep 8
end

Drücke wieder Ausführen.

Füge ein Schlagzeug hinzu:

live_loop :foo do
  sample :loop_garzul
  use_synth :prophet
  play :e1, release: 8, cutoff: rrand(70, 130)
  sleep 8
end

Ändere den Ton von e1 nach c1:

live_loop :foo do
  sample :loop_garzul
  use_synth :prophet
  play :c1, release: 8, cutoff: rrand(70, 130)
  sleep 8
end

Und jetzt höre auf, mir zuzuhören. Spiele selbst herum, viel Spaß!


9.3 - Mehrere Live-Loops

Schau Dir den folgenden Live-Loop an:

live_loop :foo do
  play 50
  sleep 1
end

Vielleicht fragst Du Dich, warum er den Namen :foo braucht. Dieser Name ist wichtig, weil er sicherstellt, dass sich dieser Live-Loop von allen anderen Live-Loops unterscheidet.

Es können nie zwei Live-Loops mit dem selben Namen gleichzeitig laufen.

Wir müssen also unterschiedliche Namen vergeben, damit wir mehrere gleichzeitig laufende Live-Loops spielen können:

live_loop :foo do
  use_synth :prophet
  play :c1, release: 8, cutoff: rrand(70, 130)
  sleep 8
end
live_loop :bar do
  sample :bd_haus
  sleep 0.5
end

Jetzt kannst Du beide Live-Loops unabhängig voneinander verändern und alles funktioniert - einfach so.

Live-Loops synchronisieren

Du hast vielleicht schon bemerkt, dass Live-Loops automatisch mit dem Thread-Cue-Mechanismus arbeiten, den wir uns im Abschnitt über Thread-Synchronisation bereits angeschaut haben. In jedem Durchlauf des Live-Loops generiert er ein neues cue-Event mit dem Namen des Loops. Anhand dieser Cues können wir für unsere Loops mit sync sicherstellen, dass sie zueinander synchron laufen, ohne dass wir das Abspielen zwischendurch stoppen müssen.

Schau Dir diesen schlecht synchronisierten Code an:

live_loop :foo do
  play :e4, release: 0.5
  sleep 0.4
end
live_loop :bar do
  sample :bd_haus
  sleep 1
end

Jetzt versuchen wir, das Timing zu korrigieren ohne den Loop zu stoppen. Zuerst reparieren wir den Loop :foo, indem wir die Sleep-Dauer zu einem Faktor von 1 machen - dafür funktioniert zum Beispiel 0.5:

live_loop :foo do
  play :e4, release: 0.5
  sleep 0.5
end
live_loop :bar do
  sample :bd_haus
  sleep 1
end

Damit sind wir aber noch nicht ganz fertig. Du hörst schon, dass die Beats nicht so recht zusammenpassen. Der Grund dafür ist, dass diese Loops out of phase sind. Das können wir reparieren, indem wir einen Loop mit dem anderen synchronisieren:

live_loop :foo do
  play :e4, release: 0.5
  sleep 0.5
end
live_loop :bar do
  sync :foo
  sample :bd_haus
  sleep 1
end

Wow, jetzt passt alles genau zusammen - und das, ohne dass wir die Loops anhalten mussten.

Jetzt fangt an und coded live in Live-Loops!


9.4 - Ticks verwenden

Beim Live-Coding wirst Du in Deinen Loops sehr oft Ringe verwenden. Aus einem Ring wirst Du Noten für Melodien, Pausen für Rhythmen, Akkordfolgen, Klangvariatonen und ähnliches entnehmen.

Ticks und Ringe

Das Tick-System ist ein sehr praktisches Werkzeug, um in einem live_loop mit Ringen zu hantieren. Du kannst damit einen Ring durchticken. Hier ein Beispiel:

counter = 0
live_loop :arp do
  play (scale :e3, :minor_pentatonic)[counter], release: 0.1
  counter += 1
  sleep 0.125
end

Das ist gleichbedeutend mit:

live_loop :arp do
  play (scale :e3, :minor_pentatonic).tick, release: 0.1
  sleep 0.125
end

In diesem Beispiel tickt der Code einmal jede Note der pentatonischen E3-Moll-Tonleiter durch. Dafür fügst Du einfach ein .tick hinter der Tonleiter-Deklaration an. Dieser Tick gilt nur für diese Live-Loop und jede Live-Loop kann seinen eigenen, unabhängigen Tick haben:

live_loop :arp do
  play (scale :e3, :minor_pentatonic).tick, release: 0.1
  sleep 0.125
end
live_loop :arp2 do
  use_synth :dsaw
  play (scale :e2, :minor_pentatonic, num_octaves: 3).tick, release: 0.25
  sleep 0.25
end

Tick

Du kannst auch den aktuellen Tick mit der Standard-Funktion tick abfragen und den Wert als Index verwenden:

live_loop :arp do
  idx = tick
  play (scale :e3, :minor_pentatonic)[idx], release: 0.1
  sleep 0.125
end

Aber eigentlich ist es viel einfacher, wenn Du .tick am Ende anfügst. Die tick-Funktion kannst Du benutzen, wenn Du aufwändigere Dinge machen möchtest; oder wenn du Ticks für andere Dinge verwenden möchtest als Werte aus einem Ring anzusteuern.

Look

Die Magie des Ticks ist, dass er sich nach jedem Aufruf erhöht, so dass Du bei der nächsten Verwendung auch den nächsten Tick bekommst. In den Beispielen findest Du einige Sonic-Pi-Codes, die .tick verwenden, schau sie Dir mal näher an. Manchmal willst Du aber den aktuellen Tick benutzen, ohne dass er gleich um einen Wert weiter tickt. Dafür gibt es die look-Funktion. Du kannst sie wie zuvor mit look als Funktion aufrufen oder mit .look an einen Ring anfügen.

Namen für Ticks

Gelegentlich wirst Du auch mal mehr als einen Tick pro Live-Loop brauchen. Gib den Ticks dafür Namen:

live_loop :arp do
  play (scale :e3, :minor_pentatonic).tick(:foo), release: 0.1
  sleep (ring 0.125, 0.25).tick(:bar)
end

In diesem Beispiel markiert ein Tick die Position in der Tonleiter, ein zweiter Tick wählt die Pausenwerte. Beide Ticks befinden sich im gleichen Live-Loop, also geben wir ihnen zur Unterscheidung eindeutige Namen. Du kennst das Prinzip schon von den Live-Loops - einfach ein : gefolgt vom Namen. Im Beispiel heißt der eine Tick :foo und der andere :bar. Willst Du die Tick-Werte mit look nur abfragen, musst Du ebenso den Namen nennen.

Mache es nicht zu kompliziert

Mit Ticks kannst Du viele großartige Dinge machen, aber versuche nicht, gleich alles in diesem Kapitel zu lernen und anzuwenden. Für den Anfang reicht es völlig, wenn Du nur durch einen Ring durchtickst und das Prinzip kennenlernst. Damit kannst Du in Deinen live_loops schon eine Menge anfangen.

Schau Dir die Dokumentation an, Du wirst dort viele praktische Beispiele entdecken. Viel Spaß beim Durchticken!


10 - Zeitzustand (Time State)

Oftmals ist es sinnvoll, Informationen zu haben, die man in verschiedenen threads or live-loops teilen möchte. Beispielsweise möchte man die aktuelle Tonart, die Geschwindigkeit (BPM) oder sogar allerlei andere abstrakte Konzepte wie die aktuelle ‘Komplexität’ teilen (die wiederum unterschiedlich in den verschiedenen Threads interpretiert werden könnte). Dabei möchten wir aber auch nicht auf die deterministischen Garantien von Sonic Pi verzichten. In anderen Worten: Wir möchten Code mit anderen teilen, aber sicherstellen, dass genau das abgespielt wird, was wir uns dabei gedacht haben. Am Ende des Kapitels 5.6. dieses Tutorials gingen wir kurz darauf ein, warum wir *Variablen nicht zwischen verschiedenen Threads teilen sollten”, um nicht die Vorhersagbarkeit zu verlieren (aufgrund von Race Conditions).

Sonic Pi’s Lösung, um auf einfache Art mit globalen Variablen in einer verhersagbaren Weise zu arbeiten, ist das neuartige System, das wir “Time State” (Zeitzustand) genannt haben. Dies klingt erstmal komplex und schwierig (solche Themen werden normalerweise erst in der Universität gelehrt), allerdings wirst du erkennen, dass es Sonic Pi unglaublich einfach macht, einen Zustand zwischen verschiedenen Threads zu teilen, und dabei dennoch Programme zu schreiben, die *thread-safe und vorhersagbar” sind.

Sag Hallo zuget und set


10.1 - Set und Get

Sonic Pi hat einen global Speicherbereich, der “Time State” genannt wird. Die beiden wesentliche Dinge, die man auf ihn an anwendet sind das Setzen von Informationen (Set) und das Holen von Information (Get).

Set

Um Informationen innerhalb des Time States zu speichern, benötigen wir zwei Dinge:

die Information, die wir speichern möchten, einen eindeutigen Namen (Schlüssel) für die Information.

Wir möchten beispielsweise die Zahl 3000 unter dem Schlüsselnamen :intensity speichern. Dies können wir mit der set function erreichen:

set :intensity, 3000

Für den Schlüssel können wir jeden Namen verwenden, den wir möchten. Haben wir einmal einen Wert unter diesem Schlüsselnamen gespeichert, wird ein neuer set -Befehl diesen überschreiben:

set :intensity, 1000
set :intensity, 3000

In dem obigen Beispiel haben wir beide Werte unter dem gleichen Schlüssel abgespeichert. Der letzte Befehl ‘gewinnt’, so dass die Zahl, die mit :intensity assoziert ist, die 3000 sein wird und somit die erste (1000) damit überschrieben wurde.

Get (Abfragen des Werts)

Um Informationen aus dem Time-State wieder zu holen, benötigen wir nun unseren Schlüssel, den wir bei set verwendet haben, also in diesem Fall :intensity. We müssen jetzt einfach get[:intensity] aufrufen und bekommen dies im log angezeigt:

print get[:intensity]  #=> prints 3000

Achte darauf, dass Aufrufe von get Informationen zurückgeben kann, die bei einem vorherigem Programm-Lauf gesetzt wurden. Wurde eine Information einmal mit Hilfe von set gespeichert, ist sie solange verfügbar bis sie entweder überschrieben wird (wie oben die 1000 mit 3000) oder Sonic Pi beendet wird.

Mehrere Threads

Der wesentliche Vorteil des Time State Systems ist, dass man auf sichere Art und Weise Informationen zwischen Threads und live-loops austauschen kann. Beispielsweise könnte eine live-loop Informationen bereitstellen, die die andere live-loop ausliest:

live_loop :setter do
  set :foo, rrand(70, 130)
  sleep 1
end
live_loop :getter do
  puts get[:foo]
  sleep 0.5
end

Das Schöne an get und set bei Threads ist, dass sie immer das gleiche Ergebnis produzieren, wenn das Programm erneut gestartet wird. Versuch’s einfach mal selbst und prüfe nach, ob die folgende Ausgabe im Log erscheint:

{run: 0, time: 0.0} 
 └─ 125.72265625
{run: 0, time: 0.5}
 └─ 125.72265625
{run: 0, time: 1.0}
 └─ 76.26220703125
{run: 0, time: 1.5}
 └─ 76.26220703125
{run: 0, time: 2.0}
 └─ 114.93408203125
{run: 0, time: 2.5}
 └─ 114.93408203125
{run: 0, time: 3.0}
 └─ 75.6048583984375
{run: 0, time: 3.5}
 └─ 75.6048583984375

Starte das Programm einfach mal mehrere Male - du wirst sehen, dass das Ergebnis immer das Gleiche ist. Das bezeichnen wir als “deterministisches” Verhalten und das ist sehr wichtig, wenn wir unsere Musik an andere weitergeben möchten und sicher sein wollen, dass die Person, die die Musik anhört, genau das gleiche Ergebnis hören wird wie wir, die es erstellt haben (ähnlich wie das Spielen einer MP3 oder das Streamen von Musik).

Ein einfaches deterministisches State System

In Abschnitt 5.6 haben wir gezeigt, wie die Nutzung von Variablen in verschiedenen Threads zu zufälligen Resultaten führen kann. Das verhindert wiederum die sichere Reproduktion von bestimmten Code wie diesem:

## Ein Beispiel für ein nicht-deterministisches Verhalten
## (aufgrund von Race Conditions, die durch
## mehrfache live-loops ausgelöst werden, die auf 
## gleiche Variable zugreifen).
##  
## Wenn Du den Code startest, siehst Du,
## dass die Liste nicht immer sortiert ist.
a = (ring 6, 5, 4, 3, 2, 1)
live_loop :shuffled do
  a = a.shuffle
  sleep 0.5
end
live_loop :sorted do
  a = a.sort
  sleep 0.5
  puts "sorted: ", a
end

Schauen wir mal, wie wir get und set einsetzen können:

## Ein Beispiel für ein deterministisches Verhalten
## (trotz des gleichzeitigen Zugriff auf einen gemeinsamen Zustand) 
## mit Hilfe des Sonic Pi Time State Systems
##
## Wenn dieser Code gestartet wird,
## wird die Liste immer sortiert ausgegeben.
set :a, (ring 6, 5, 4, 3, 2, 1)
live_loop :shuffled do
  set :a, get[:a].shuffle
  sleep 0.5
end
live_loop :sorted do
  set :a, get[:a].sort
  sleep 0.5
  puts "sorted: ", get[:a]
end

Beachte, dass dieser Code fast identisch zu dem Code ist, der die Variable zum Teilen der Information verwendet hat. Allerdings verhält er sich so, wie man es sich bei einem typischen Sonic Pi Code erwartet - er verhält sich immer gleich in diesem Fall dank des Time State Systems.

Deshalb verwende immer get und set, wenn Du Daten zwischen live-loops und Threads teilen möchtest anstatt Variablen zu verwenden.


10.2 - Sync(hronisation)

In Kapitel 5.7 beschäftigten wir uns mit den Funktionen cue und sync, um Threads zu synchronisieren. Dort wurde allerdings nicht erklärt, was es mit dem Time State System auf sich hat, das diese Funktionalität zur Verfügung stellt. Tatsächlich ist set eine Variation von cue und baut auf die gleiche Kernfunktionalität auf, Informationen im Time State System zu hinterlegen. Zusätzlich ist sync`so gestaltet, dass es einfach mit dem Time State funktioniert - es kann auf jegliche Information, die im Time State gespeichert ist, kann auch synchronisiert werden. Mit anderen Worten: wir synchronisieren auf Ereignisse, die im Time State gespeichert werden.

Warten auf Ereignisse (Events)

Lass uns mal anschauen, wie man syncverwendet, um auf neue Ereignisse zu warten, die dem Time State hinzugefügt werden:

in_thread do
  sync :foo
  sample :ambi_lunar_land
end
sleep 2
set :foo, 1

In diesem Beispiel erstellen wir zuerst einen Thread, der darauf wartet, dass ein ‘:foo’ event zum Time State hinzugefügt wird. Nach dieser Threaderstellung (Deklaration) schlafen wir für 2 beats und dann setzen wir mit ‘set’-Befehl den ‘:foo’-Wert auf ‘1’. Diese Aktion ‘releases’ (beendet) den ‘sync’ und geht weiter zur nächsten Zeile, wo das ‘:ambi_lunar_land’-Sample getriggert wird.

Beachte, dass sync immer auf *zukünftige Ereignisse” wartet and den Thread genau an dieser Stelle so lange blockiert, bis das Ereignis eingetreten ist. Zusätzlich erbt der (gesyncte) Thread die logische Zeite des anderen Threads, der ihn mit Hilfe von set oder cue getriggert (angestossen) hat. Das heißt, dass man durch dieses Verfahren auch die Zeit von zwei Threads synchronisieren kann.

Übergabe von Werten in die Zukunft

In diesem Beispiel setzen wir ‘:foo’ auf den Wert ‘1’, mit dem wir dann nichts gemacht haben. Diesen Wert können wir im Thread auch wieder abfragen (‘get’), in dem wir den Befehl ‘sync’ aufrufen:

in_thread do
  amp = sync :foo
  sample :ambi_lunar_land, amp: amp
end
sleep 2
set :foo, 0.5

Beachte, dass Werte, die mit Hilfe von set und `cue übergeben werden, “thread safe” sein müssen. Das bedeutet, dass nur Werte übergeben werden dürfen, die sich in der Zukunft nicht verändern können wie zum Beispiel “unveränderliche Zeichenketten” (immutable strings), Zahlen, Symbole oder eingefrorene Zeichenketten (frozen strings). Sonic Pi wirft einen Fehler, falls ein Wert verwendet wird, der nicht dieser Vorgabe entspricht.


10.3 - Muster-Erkennung

Beim Einlesen und Setzen von Informationen in den Zeitstatus ist es möglich, komplexere Tasten als einfache Symbole wie :foo und :bar zu verwenden. Sie können auch URL-Style-Strings wie "/foo/bar/baz" verwenden. Sobald wir mit Pfaden arbeiten, können wir die Vorteile von Sonic Pi’s ausgeklügeltem Pattern-Matching-System nutzen, um Get und Sync mit ähnlichen' statt identischen’ Pfaden zu nutzen. Schauen wir uns das mal an.

Erkennen von Pfad-Segmenten

Angenommen wir möchten auf ein Ereignis warten, das die folgenden drei Pfad-Segmente enthält:

sync  "/*/*/*"

Hiermit kann man auf jegliches Time State Ereignis syncen, dass aus exakt drei Pfadsegmenten besteht, unabhängig davon, was genau der Inhalt der Segmente ist. Schauen wir uns das an einem Beispiel an:

cue "/foo/bar/baz" cue "/foo/baz/quux" cue "/eggs/beans/toast" cue "/moog/synths/rule"

Allerdings wird es nicht auf Ereignisse “matchen”, die weniger oder mehr Pfad-Segmente hätten. Die folgenden würden nicht erkannt und damit kein Ereignis triggern:

cue "/foo/bar" cue "/foo/baz/quux/quaax" cue "/eggs"

Jeder * bedeutet irgendein Inhalt. Das heisst, wie können Pfade mit nur einem Segment mit /* oder Pfade aus fünf Segmenten mit /*/*/*/*/* erkennen

Erkennen von Teilsegmenten

Wenn wir wissen, womit das Segment beginnen oder enden wird, können wir zusätzlich zu einem Teilsegmentnamen ein * verwenden. Zum Beispiel: "/foo/b*/baz"passt auf jeden Pfad, der drei Segmente hat, von denen das erste foo, das letzte baz und das mittlere Segment alles sein können, was mit b beginnt. Also, es würde passen:

cue "/foo/bar/baz" cue "/foo/baz/baz" cue "/foo/beans/baz"

Allerdings erkennt es nicht das Folgende:

cue "/foo/flibble/baz" cue "/foo/abaz/baz" cue "/foo/beans/baz/eggs"

Du kannst das * auch an den Anfang des Segments setzen, um die letzten Zeichen eines Segments anzugeben: "/foo/*zz/baz", das jedem 3-Segment cue oder set entspricht, wobei das erste Segment foo ist, das letzte baz ist und das mittlere Segment mit zz endet, wie "cue "/foo/whizz/baz".

Erkennen von verschachtelten Pfadsegmenten

Manchmal weiß man nicht, wie viele Pfadsegmente man zuordnen möchte. In diesen Fällen kannst Du den mächtigen Doppelstern: ** wie "/foo/**/**/baz" verwenden, der übereinstimmt:

cue "/foo/bar/baz" cue "/foo/bar/beans/baz" cue "/foo/baz" cue "/foo/a/b/c/d/e/f/baz"

Erkennen von einzelnen Buchstaben

Du kann das ? verwenden, um auf ein bestimmtes einzelnes Zeichen zu erkennen. "/?oo/bar/baz" passt auf:

cue "/foo/bar/baz" cue "/goo/bar/baz" cue "/too/bar/baz" cue "/woo/bar/baz"

Erkennen von mehrfachen Wörtern

foo

cue "/foo/bar/quux" cue "/foo/beans/quux" cue "/foo/eggs/quux"

Erkennen von mehrfachen Zeichen

foo

cue "/foo/aux/baz" cue "/foo/bux/baz" cue "/foo/cux/baz"

foo

cue "/foo/aux/baz" cue "/foo/bux/baz" cue "/foo/cux/baz" cue "/foo/dux/baz" cue "/foo/eux/baz"

Kombinieren von Mustererkennungen

Wenn du sync oder get aufrufst, kannst du Mustererkenner in beliebiger Reihenfolge kombinieren um jedes beliebige Time State Event, das durch cue oder set erzeugt wurde zu erkennen. Lass uns ein verrücktes Beispiel anschauen:

sample :loop_amen

OSC Mustererkennung

Für die Neugierigen, diese Regeln zur Mustererkennung basieren auf der Open Sound Control Spezifikation zur Mustererkennung, die hier im Detail erklärt ist: http://opensoundcontrol.org/spec-1_0


11 - MIDI

Nachdem du es gemeistert hast Code in Musik umzuwandeln, fragst du dich vielleicht - was kommt als nächstes? Manchmal sind die Einschränkungen nur mit Sonic Pis Syntax und Soundsystem zu arbeiten aufregend und versetzen dich in eine neue kreative Lage. Manchmal jedoch ist es notwendig aus dem Programmcode in die echte Welt auszubrechen. Wir brauchen zwei zusätzliche Dinge:

Die Möglichkeit Ereignisse in der echten Welt in Sonic Pi Events umzuwandeln, um damit zu programmieren Die Möglichkeit Sonic Pis starkes Zeitmodell und Semantik nutzen zu können um Objekte in der echten Welt zu kontrollieren und zu beeinflussen

Glücklicherweise gibt es ein Protokoll, das schon seit den 80ern existiert, das genau diese Art von Interaktion möglich macht - MIDI. Es gibt eine unglaubliche Anzahl an externen Geräten unter anderem Keyboards, Controller, Sequencer, und professionelle Audio Software, die alle MIDI unterstützen. Wir können MIDI nutzen um Daten zu empfangen und auch um Daten zu senden.

Sonic Pi stellt komplette Unterstützung für das MIDI Protokoll bereit, was es dir erlaubt deinen Live Code mit der echten Welt zu verbinden. Lass uns das weiter untersuchen…


11.1 - MIDI In

In diesem Abschnitt werden wir lernen, wie man einen MIDI Controller verbindet, sodass er Ereignisse an Sonic Pi sendet, um unsere Synths und Töne zu kontrollieren. Nimm dir einen MIDI Controller, wie beispielsweise ein Keyboard oder eine Control Oberfläche und lass uns loslegen!

Verbinde einen MIDI Controller

Um Informationen von einem externen MIDI Gerät in Sonic Pi zu bekommen müssen wir es erst an unseren Computer anschließen. Normalerweise wird das mit einer USB Verbindung passieren, aber ältere Geräte haben einen 5-Pin DIN Stecker für den du Hardwareunterstützung in deinem Computer brauchst(manche Soundkarten haben zum Beispiel MIDI DIN Stecker). Nachdem du dein Gerät angeschlossen hast, starte Sonic Pi und wirf einen Blick auf den EA Abschnitt in den Einstellungen. Du solltest dein Gerät dort aufgelistet sehen. Wenn nicht, drücke den “Reset MIDI” button und schaue ob es auftaucht. Wenn du immer noch nichts siehst ist der nächste Schritt zu versuchen in der MIDI Konfiguration deines Betriebssystems nachzusehen um zu sehen ob sie dein Gerät erkennt. Wenn das fehlschlägt, kannst du gerne im öffentlichen Chat Fragen stellen: http://gitter.im/samaaron/sonic-pi

Erhalte MIDI Ereignisse

Nachdem dein Gerät verbunden ist, empfängt Sonic Pi automatisch Events. Du kannst das selbst nachprüfen indem du dein MIDI Gerät bedienst und auf den Cue Logger unten rechts im Anwendungsfenster unterhalb des Logs schaust (wenn das nicht sichtbar ist gehe zu Einstellungen->Editor->Ein-/Ausblenden und aktiviere die ‘Zeige cue Log’ checkbox). Du wirst einen Datenstrom an Events sehen wie:

/midi:nanokey2_keyboard:0:1/note_off  [55, 64]
/midi:nanokey2_keyboard:0:1/note_on   [53, 102]
/midi:nanokey2_keyboard:0:1/note_off  [57, 64]
/midi:nanokey2_keyboard:0:1/note_off  [53, 64]
/midi:nanokey2_keyboard:0:1/note_on   [57, 87]
/midi:nanokey2_keyboard:0:1/note_on   [55, 81]
/midi:nanokey2_keyboard:0:1/note_on   [53, 96]
/midi:nanokey2_keyboard:0:1/note_off  [55, 64]

Wenn du einen Datenstrom an Nachrichten wie diesen sehen kannst, hast du dein MIDI Gerät erfolgreich verbunden. Glückwunsch, lass uns schauen, was wir damit machen können!

MIDI Time State

Diese Events sind in zwei Abschnitte unterteilt. Erstens gibt es den Namen des Events, wie /midi:nanokey2_keyboard:0:1/note_on und zweitens gibt es die Werte des Events wie [18, 62]. Interessanter Weise sind das die zwei Dinge, die wir brauchen, um Informationen in Time State zu speichern. Sonic Pi fügt eingegangene MIDI Events automatisch in Time State ein. Das bedeutet du kannst auf den neuesten MIDI Wert mit get zugreifen und auch mit sync auf den nächsten MIDI Wert warten indem du alles was du in Abschnitt 10 dieses Tutorials gelernt hast nutzt.

Effekte steuern

Jetzt nachdem wir ein MIDI Gerät verbunden, seine Events im cue Log gesehen und entdeckt haben, dass unser Wissen über Time State alles ist, was wir zum Arbeiten mit Events brauchen, können wir anfangen Spaß zu haben. Lass uns ein einfaches MIDI Klavier bauen:

live_loop :midi_piano do
  note, velocity = sync "/midi:nanokey2_keyboard:0:1/note_on"
  synth :piano, note: note
end

Es passieren einige Dinge im obigen Code, auch einiges Problematisches. Zuerst haben wir einfach einen live_loop, der für immer den Code zwischen dem do/end Block wiederholt. Das wurde in Abschnitt 9.2 eingeführt. Danach rufen wir sync auf, um auf das nächste Time State Event zu warten. Wir benutzen einen String, der die MIDI Nachricht repräsentiert, nach der wir suchen (welcher der selbe ist, wie der, der im cue Logger angezeigt wurde). Bemerke, dass dieser lange String von Sonic Pis Autovervollständigung vervollständigt wird, sodass du ihn nicht komplett von Hand eintippen musst. Im Log haben wir gesehen, dass es zwei Werte für jedes MIDI “note on” Event gibt, also weisen wir das Ergebnis an zwei separate Variablen note und velocity zu. Schlussendlich lösen wir den :piano Synth aus und übergeben unsere Note.

Jetzt probiere du es. Gib den obigen Code ein und ersetzte den sync Schlüssel mit einer Zeichenkette die deinem spezifischen MIDI Gerät entspricht und drücke Ausführen. Hey presto, du hast ein funktionierendes Klavier! Du bemerkt jedoch wahrscheinlich ein paar Probleme: Erstens sind alle Noten gleich laut, unabhängig davon, wie stark du das Klavier anschlägst. Das kann einfach behoben werden indem wir den velocity MIDI Wert nutzen und in eine Amplitude umwandeln. Nachdem MIDI einen Wertebereich von 0-127 hat müssen wir, um diese Zahl in einen Wert zwischen 0->1 zu konvertieren, nur durch 127 teilen:

live_loop :midi_piano do
  note, velocity = sync "/midi:nanokey2_keyboard:0:1/note_on"
  synth :piano, note: note, amp: velocity / 127.0
end

Aktualisiere den Code und drücke erneut auf Ausführen. Jetzt wird der Anschlag des Klaviers berücksichtigt. Als nächstes lass uns diese ärgerliche Pause loswerden.

Latenz entfernen

Bevor wir die Pause entfernen können, müssen wir erfahren, warum sie da ist. Um alle Synths und FX auf einer Vielzahl an verschiedenen unterschiedlich starken CPUs gut getaktet zu halten, setzt Sonic Pi das Audio standardmäßig 0,5s im voraus an. (Beachte, dass diese zusätzliche Latenz über die fns set_sched_ahead_time! und use_sched_ahead_time konfiguriert werden kann). Diese 0,5s Latenz wird zu unseren :piano Synth Auslösern hinzugefügt, wie es bei allen Synths, die von Sonic Pi ausgelöst werden, passiert. Normalerweise möchten wir diese Latenz wirklich, weil sie bedeutet, dass alle Synths gut getaktet werden. Das ergibt jedoch nur Sinn bei Synths, die von Code durch play und sleep ausgelöst werden. In diesem Fall lösen wir den :piano Synth mit unserem externen MIDI Gerät aus und möchten daher nicht, dass Sonic Pi das Timing für uns kontrolliert. Wir können diese Latenz mit dem Befehl use_real_time ausschalten, welches die Latenz für den aktuellen Thread abschaltet. Das bedeutet, dass du den Echtzeitmodus für Live-Loops benutzen kannst, die ihr Timing durch syncen mit externen Geräten kontrollieren, und die normale Latenz für alle anderen Live-Loops behalten kannst. Schauen wir mal:

live_loop :midi_piano do
  use_real_time
  note, velocity = sync "/midi:nanokey2_keyboard:0:1/note_on"
  synth :piano, note: note, amp: velocity / 127.0
end

Passe deinen Code an, sodass er dem obigen Code entspricht und drücke erneut auf Ausführen. Jetzt haben wir ein Klavier mit niedriger Latenz und variablem Anschlag in nur 5 Zeilen programmiert. War das nicht einfach!

Werte erhalten

Schließlich, nachdem unsere MIDI Events direkt in den Time State gehen, können wir die get fn benutzen um den letzten gesehenen Wert zu erhalten. Das blockiert nicht den aktuellen Thread und gibt nil zurück, wenn es keinen Wert gibt (was du überschreiben kannst, indem du einen Standardwert übergibst - schaue in die Dokumentation für get). Erinnere dich, dass du get in jedem Thread zu jeder Zeit aufrufen kannst, um den letzten zutreffenden Time State Wert zu sehen. Du kannst sogar time_warp benutzen um in der Zeit zurück zu reisen und get aufrufen um vergangene Events zu sehen…

Du hast jetzt die Kontrolle

Das spannende ist jetzt, dass du jetzt die gleichen Code Strukturen benutzen kannst um MIDI Informationen von irgendeinem MIDI Gerät zu syncen und geten und mit den Werten tun kannst was auch immer du möchtest. Du kannst jetzt entscheiden was dein MIDI Gerät tun wird!


11.2 - MIDI Ausgabe

Neben dem Empfang von MIDI-Events können wir auch MIDI-Events aussenden, um externe Hardware-Synths, Keyboards und andere Geräte zu triggern und zu steuern. Sonic Pi bietet einen vollständigen Satz von Funktionen zum Senden verschiedener MIDI-Nachrichten wie z.B:

Note on - midi_note_on Note off - midi_note_off Control change - midi_cc Tonhöhenveränderung - midi_pitch_bend Taktgeber - midi_clock_tick

Es gibt auch noch viele weitere MIDI-Meldungen – schau in der API-Dokumentation nach all den anderen Funktionen, die mit midi_anfangen.

Mit einem MIDI-Gerät verbinden

Um eine MIDI-Nachricht an ein externes Gerät zu senden, müssen wir es zuerst angeschlossen haben. Schau Dir den Unterabschnitt ‘Anschließen eines MIDI-Instruments’ in Abschnitt 11.1 für weitere Details an. Beachte, dass bei Verwendung von USB der Anschluss an ein Gerät, an das Du sendest (und nicht von dem Du empfängst), die gleiche Prozedur ist. Wenn Du jedoch die klassischen DIN-Anschlüsse verwendest, stell sicher, dass Du den MIDI-Ausgang Deines Computers verwendest. Du solltest Dein MIDI-Gerät in den Einstellungen sehen.

MIDI-Ereignisse senden

Die verschiedenen midi_* Funktionen verhalten sich genauso wie play :

midi_note_on :e3, 50

Dieser sendet eine MIDI-Note auf Event an das angeschlossene MIDI-Gerät mit Velocity 50. (Beachte, dass Sonic Pi Noten in der Form :e3 automatisch in ihre entsprechende MIDI-Nummer wie z.B. 52 in diesem Fall konvertiert).

Wenn Dein MIDI-Gerät ein Synthesizer ist, solltest Du hören können, wie er eine Note spielt. Um dies zu deaktivieren, verwende midi_note_off:

midi_note_off :e3

Auswahl eines MIDI-Gerätes

Standardmäßig wird Sonic Pi jede MIDI Nachricht an alle verbundenen Geräte auf allen MIDI Kanälen senden, sodass es einfacher ist, mit einem einzelnen verbundenen Gerät zu arbeiten, ohne etwas einstellen zu müssen. Allerdings kann es sein, dass ein MIDI Gerät Nachrichten auf verschiedenen Kanälen unterschiedlich handhabt (zum Beispiel könnte jede Note einen eigenen Kanal haben), oder Du möchtest mehr als ein MIDI Gerät anschließen. Bei etwas umfangreicheren Setups, möchtest du wahrscheinlich genauer bestimmen können, welches Gerät welche Nachricht auf welchem Kanal erhält.

Wir können das Gerät, welches angesprochen werden soll mit der port: Option angeben, indem wir den Gerätenamen verwenden, der in den Einstellungen angezeigt wird:

midi_note_on :e3, port: "moog_minitaur"

Wir können auch angeben, an welchen Kanal gesendet werden soll, in dem wir die Option channel: (mit einem Wert im Bereich 1-16) verwenden:

midi_note_on :e3, channel: 3

Natürlich können wir auch beides zur gleichen Zeit festlegen, um an ein bestimmtes Gerät auf einen bestimmten Kanal zu senden:

midi_note_on :e3, port: "moog_minitaur", channel: 5

MIDI-Studio

Schlussendlich ist es auch sehr spannend, den Audio-Ausgang deines MIDI Synthesizers mit einem deiner Audio-Eingänge deiner Soundkarte zu verbinden. Dann kannst du deinen Synthesizer mit den midi_*Funktionen steuern und auch die erzeugten Sounds mit live_audiound Effekten verändern:

reverb,

(Die Funktion midi ist eine handliche Vereinfachung um note on und note off Events mit einem einzigen Befehl zu senden. Für weitere Informationen schaue einfach in die Dokumentation).


12 - OSC

Ein (zu MIDI) zusätzlicher Weg, um Information aus Sonic Pi hinaus und hinein zu bekommen, ist OSC - Open Sound Control, ein einfaches Netzwerkprotokoll. Es lässt dich Nachrichten an externe Programme senden und empfangen (jeweils auf dem gleichen oder verschiedenen Computern) und eröffnet somit neue Steuerungsmöglichkeiten weit über MIDI hinaus, welches durch sein 1980er Design Einschränkungen hat.

Zum Beispiel könntest Du ein Programm in einer anderen Programmiersprache wie Python oder sclang schreiben, das OSC Nachrichten sendet oder empfängt und direkt mit Sonic Pi zusammenarbeitet. Es gibt OSC Bibliotheken für die allermeisten gebräuchlichen Programmiersprachen. Was Du damit tun kannst, ist nur durch deine Vorstellungskraft begrenzt.


12.1 - Receiving OSC

Zeitzustand (Time State)

A Basic OSC Listener

Lass uns einen einfachen OSC listener (Code der auf OSC Nachrichten hört) bauen:

live_loop :foo do 
  use_real_time
  a, b, c = sync "/osc*/trigger/prophet"
  synth :prophet, note: a, cutoff: b, sustain: c
end

In diesem Beispiel haben wir als OSC Pfad, auf dem wir auf Nachrichten lauschen, "/osc*/trigger/prophet" festgelegt. Dafür können wir jeden beliebigen validen OSC Pfad verwenden (alle Buchstaben und Zahlen sind erlaubt, das /wird wie in einer URL verwendet um Wörter zu trennen). Das Präfix /osc wird von Sonic Pi allen eingehenden OSC Nachrichten angefügt, also müssen wir eine OSC Nachricht mit dem Pfad /trigger/prophet senden, sodass der sync Befehl beendet und der prophet Synth gespielt wird.

OSC Nachrichten an Sonic Pi senden

Wir können OSC Nachrichten von jeder beliebigen Programmiersprache an Sonic Pi senden, die Unterstützung (eine Bibliothek) für OSC bietet. Wenn wir zum Beispiel eine OSC Nachricht aus Python schicken wollen, machen wir vielleicht so etwas:

from pythonosc import osc_message_builder 
from pythonosc import udp_client
sender = udp_client.SimpleUDPClient('127.0.0.1', 4560)
sender.send_message('/trigger/prophet', [70, 100, 8])

Oder wenn wir eine OSC Nachricht aus Clojure senden, sieht das vielleicht so aus:

(use 'overtone.core) 
(def c (osc-client "127.0.0.1" 4560))
(osc-send c "/trigger/prophet" 70 100 8)

Von anderen Computern empfangen

Aus Sicherheitsgründen empfängt Sonic Pi standardmäßig keine OSC Nachrichten von anderen Computern. Trotzdem kann man das unter Einstellungen->IO->Netzwerk->Empfange OSC von entfernten Computern aktivieren. Sobald du dieses Feature aktiviert hast, kannst du OSC Nachrichten von beliebigen Computern in deinem Netzwerk empfangen. Dafür muss der Sender deine IP-Adresse (ein eindeutiger Bezeichner für deinen Computer in deinem Netzwerk - wie eine Telefonnummer oder E-Mail-Adresse) kennen. Du kannst diese herausfinden, indem Du in den Einstellungen den IO-Reiter aufrufst. (Falls dein Computer mehrere IP-Adressen haben sollte, kannst du alle sehen, wenn du den Mauszeiger über der angezeigten ruhen lässt).

Manche Programme, wie TouchOSC für iPhone und Android, unterstützen das Senden von OSC Nachrichten von Haus aus. Also kannst du direkt, wenn du auf OSC Nachrichten von entfernten Computer hörst und deine IP-Adresse kennst, damit beginnen von Apps wie TouchOSC OSC Nachrichten zu senden und deine eigenen Steuerungen mit Slidern, Knöpfen, Drehknöpfen usw. zu bauen. Dies bietet Dir eine enorme Bandbreite an Eingabemöglichkeiten.


12.2 - OSC Nachrichten senden

In addition to receiving OSC and working with it using Time State, we can also send out OSC messages in time with our music (just like we can send out MIDI messages in time with our music). We just need to know which IP address and port we’re sending to. Let’s give it a try:

use_osc "localhost", 4560
osc "/hello/world"

If you run the code above, you’ll notice that Sonic Pi is sending itself an OSC message! This is because we set the IP address to the current machine and the port to the default OSC in port. This is essentially the same as posting a letter to yourself - the OSC packet is created, leaves Sonic Pi, gets to the network stack of the operating system which then routes the packed back to Sonic Pi and then it’s received as a standard OSC message and is visible in the cue logger as the incoming message /osc:127.0.0.1:4560/hello/world. (Notice how Sonic Pi automatically prefixes all incoming OSC messages with /osc and then the hostname and port of the sender.)

Sending OSC to other programs

Of course, sending OSC messages to ourselves may be fun but it’s not that useful. The real benefit starts when we send messages to other programs:

use_osc "localhost", 123456
osc "/hello/world"

In this case we’re assuming there’s another program on the same machine listening to port 123456. If there is, then it will receive a "/hello/world OSC message with which it can do what it wants.

Wenn Dein Programm auf einer anderen Maschine läuft, müssen wir deren IP-Adresse kennen, die wir anstelle von "localhost"verwenden:

use_osc "192.168.10.23", 123456
osc "/hello/world"

Now we can send OSC messages to any device reachable to us via our local networks and even the internet!


13 - Multikanal-Audio

So far, in terms of sound production, we’ve explored triggering synths and recorded sounds via the fns play, synth and sample. These have then generated audio which has played through our stereo speaker system. However, many computers also have the ability to input sound, perhaps through a microphone, in addition to the ability to send sound out to more than two speakers. Often, this capability is made possible through the use of an external sound card - these are available for all platforms. In this section of the tutorial we’ll take a look at how we can take advantage of these external sound cards and effortlessly work with multiple channels of audio in and out of Sonic Pi.


13.1 - Sound In

One simple (and perhaps familiar) way of accessing sound inputs is using our friend synth by specifying the :sound_in synth:

synth :sound_in

This will operate just like any synth such as synth :dsaw with the exception that the audio generated will be read directly from the first input of your system’s sound card. On laptops, this is typically the built-in microphone, but if you have an external sound card, you can plug any audio input to the first input.

Die Dauer erhöhen

One thing you might notice is that just like synth :dsaw the :sound_in synth only lasts for 1 beat as it has a standard envelope. If you’d like to keep it open for a little longer, change the ADSR envelope settings. For example the following will keep the synth open for 8 beats before closing the connection:

synth :sound_in, sustain: 8

FX hinzufügen

Of course, just like any normal synth, you can easily layer on effects with the FX block:

with_fx :reverb do
  with_fx :distortion do
    synth :sound_in, sustain: 8
  end
end

If you have plugged in a guitar to your first input, you should be able to hear it with distortion and reverb until the synth terminates as expected.

You are free to use the :sound_in synth as many times as you like concurrently (just like you would do with any normal synth). For example, the following will play two :sound_in synths at the same time - one through distortion and one through reverb:

with_fx :distortion do
  synth :sound_in, sustain: 8
end
with_fx :reverb do  
  synth :sound_in, sustain: 8
end

Mehrteilige Ketten

You can select which audio input you want to play with the input: opt. You can also specify a stereo input (two consecutive inputs) using the :sound_in_stereo synth. For example, if you have a sound card with at least three inputs, you can treat the first two as a stereo stream and add distortion and the third as a mono stream and add reverb with the following code:

with_fx :distortion do
  synth :sound_in_stereo, sustain: 8, input: 1
end
with_fx :reverb do  
  synth :sound_in, sustain: 8, input: 3
end

Mögliche Probleme

However, although this is a useful technique, there are a couple of limitations to this approach. Firstly, it only works for a specific duration (due to it having an ADSR envelope) and secondly, there’s no way to switch the FX around once the synth has been triggered. Both of these things are typical requests when working with external audio feeds such as microphones, guitars and external synthesisers. We’ll therefore take a look at Sonic Pi’s solution to the problem of manipulating a (potentially) infinite stream of live audio input: live_audio.


13.2 - Live-Coding

The :sound_in synth as described in the previous section provides a very flexible and familiar method for working with input audio. However, as also discussed it has a few issues when working with a single input of audio as a single instrument (such as a voice or guitar). By far the best approach to working with a single continuous stream of audio is to use live_audio.

A Named Audio Input

live_audio shares a couple of core design constraints with live_loop (hence the similar name). Firstly it must have a unique name and secondly only one live_audio stream with that name may exist at any one time. Let’s take a look:

foo

Dieser Code verhält sich ähnlich zu synth :sound_in, mit einigen wesentlichen Unterschieden: er läuft ununterbrochen (bis Du ihn ausdrücklich stoppst) und Du kannst ihn dynamisch in neue FX-Inhalte schieben.

Mit FX arbeiten

On initial triggering live_audio works exactly as you might expect it to work with FX. For example, to start a live audio stream with added reverb simply use a :reverb FX block:

foo

However, given that live_audio runs forever (at least until you stop it) it would be pretty limiting if, like typical synths, the live audio was bound within the :reverb FX for its entire existence. Luckily this is not the case and it was designed to be easy to move between different FX. Let’s try it. Run the code above to hear live audio coming directly from the first input of your sound card. Note, if you’re using a laptop, this will typically be out of your built-in microphone, so it’s recommended to use headphones to stop feedback.

Jetzt, während Du noch den Ton live von der Soundkarte mit Reverb hörst, ändere den Code so um:

foo

Und nun, drück Run, und sofort wirst Du den Sound mit dem Echo-FX hören und nicht mehr mit Reverb. Wenn Du beides möchtest, ändere einfach wieder den Code und drück Run:

foo

foo

Stopping live audio

Unlike standard synths, as live_audio has no envelope, it will continue running forever (even if you delete the code, just like a function is still defined in memory if you delete the code in the editor). To stop it, you need to use the :stop arg:

foo

It can easily be restarted by calling it without the :stop arg again:

foo

Additionally all running live audio synths are stopped when you hit the global Stop button (as with all other running synths and FX).

Stereo-Eingang

With respect to audio channels, by default live_audio acts similarly to the :sound_in synth in that it takes a single mono input stream of audio and converts it to a stereo stream using the specified panning. However, just like :sound_in_stereo it’s also possible to tell live_audio to read two consecutive audio inputs and treat them as the left and right channels directly. This is achieved via the :stereo opt. For example, to treat input 2 as the left signal and input 3 as the right signal, you need to configure the input: opt to 2 and enable stereo mode as follows:

foo

Note that once you have started a live audio stream in stereo mode, you cannot change it to mono without stopping and starting. Similarly, if you start it in the default mono mode, you can’t switch to stereo without starting and stopping the stream.


13.3 - Soundausgabe

So far in this section we’ve looked at how to get multiple streams of audio into Sonic Pi - either through the use of the :sound_in synth or via the powerful live_audio system. In addition to working with multiple streams of input audio, Sonic Pi can also output multiple streams of audio. This is achieved via the :sound_out FX.

Output contexts

Let’s quickly recap on how Sonic Pi’s synths and FX output their audio to their current FX context. For example, consider the following:

with_fx :reverb do    # C
  with_fx :echo do    # B
    sample :bd_haus   # A
  end
end

The simplest way to understand what’s happening with the audio stream is to start at the innermost audio context and work our way out. In this case, the innermost context is labelled A and is the :bd_haus sample being triggered. The audio for this goes directly into its context which is B - the :echo FX. This then adds echo to the incoming audio and outputs it to its context which is C - the :reverb FX. This then adds reverb to the incoming audio and outputs to its context which is the top level - the left and right speakers (outputs 1 and 2 in your audio card). The audio flows outwards with a stereo signal all the way through.

Sound Out FX

The above behaviour is true for all synths (including live_audio) and the majority of FX with the exception of :sound_out. The :sound_out FX does two things. Firstly it outputs its audio to its external context as described above. Secondly it also outputs its audio directly to an output on your sound card. Let’s take a look:

with_fx :reverb do      # C
  with_fx :sound_out, output: 3 do # B
    sample :bd_haus     # A
  end
end

In this example, our :bd_haus sample outputs its audio to its external context which is the :sound_out FX. This in turn outputs its audio to its external context the :reverb FX (as expected). However, it also outputs a mono mix to the 3rd output of the system’s soundcard. The audio generated within :sound_out therefore has two destinations - the :reverb FX and audio card output 3.

Mono- und Stereoausgabe

Auswählen (choose)

Direct Out

As we have also seen, the default behaviour for :sound_out and :sound_out_stereo is to send the audio both to their external context (as is typical of all FX) and to the specified output on your soundcard. However, occasionally you may wish to only send to the output on your soundcard and not to the external context (and therefore not have any chance of the sound being mixed and sent to the standard output channels 1 and 2). This is possible by using the standard FX opt amp: which operates on the audio after the FX has been able to manipulate the audio:

sample :loop_amen

In the above example, the :loop_amen sample is sent to its outer context, the :sound_out FX. This then sends a mono mix to audio card output 3 and then multiplies the audio by 0 which essentially silences it. It is this silenced signal which is then sent out to the :sound_out’s outer context which is the standard output. Therefore with this code, the default output channels will not receive any audio, and channel 3 will receive a mono mix of the amen drum break.


12 - Abschluss

Damit bist Du am Ende des Tutorials von Sonic Pi angekommen. Hoffentlich hast Du etwas dabei gelernt. Falls Du das Gefühl hast, nicht alles verstanden zu haben - keine Angst, spiele einfach weiter und hab Spaß dabei. Dann wird immer mehr hängen bleiben. Und greife einfach auf das Tutorial zurück, wenn Du eine Frage hast, die vielleicht in einem der vorherigen Kapitel behandelt wurde.

Sind noch Fragen übrig, die über die Einführung hinausgehen? Du kannst damit ins Sonic Pi Forum kommen und Dein Anliegen dort beschreiben. Dort sind viele nette Menschen, die Dir gerne weiterhelfen.

Du bist auch eingeladen, Dir den Rest der Dokumentation im Hilfe-System genauer anzuschauen. Es gibt noch eine Reihe von Features, die in dieser Einführung nicht behandelt wurden und darauf warten, von Dir entdeckt zu werden.

Also: Spiele herum, hab Spaß, teile Deinen Code, tritt vor Freunden auf, zeige was Du auf dem Monitor hast und nicht vergessen:

There are no mistakes, only opportunities. (Es gibt keine Fehler, nur Möglichkeiten.)

Sam Aaron


- Artikel aus MagPi

Appendix A sammelt Artikel über Sonic Pi, die für das MagPi-Magazin geschrieben wurden.

Steig’ in die Themen ein

Diese Artikel müssen nicht in bestimmter Reihenfolge gelesen werden und wiederholen einiges aus dem Tutorial. Es geht weniger um einen Gesamtüberblick von Sonic Pi, sondern eher darum, einen Aspekt genauer anzusehen und diesen leicht verständlich darzustellen.

MagPi lesen

Du kannst sie in wunderbar gesetzter Form in der Rubrik freie PDF-Downloads des MagPi sehen: https://www.raspberrypi.org/magpi/

Schlage ein Thema vor

Wenn Dich ein Thema interessiert, welches noch nicht behandelt wurde - warum nicht eines vorschlagen? Der einfachste Weg ist ein Tweet mit deinem Vorschlag an @Sonic_Pi. Man kann nie wissen - Dein Vorschlag könnte das Thema des nächsten Artikels sein!


- Fünf Top-Tipps

1. Es gibt keine Fehler

Die wichtigste Lektion mit Sonic Pi ist, dass es wirklich keine Fehler gibt. Der beste Weg zu lernen, ist einfach es zu versuchen und zu versuchen und zu versuchen. Probiere viele verschiedene Dinge aus, kümmere Dich nicht darum, ob Dein Code gut oder schlecht klingt und fange an mit so vielen unterschiedlichen Synths, FXs und Optionen wie möglich zu experimentieren. Du wirst viele Dinge entdecken, die Dich zum Lachen bringen, weil sie einfach furchtbar klingen und auch richtige Edelsteine, die einfach toll klingen. Was Dir nicht gefällt, lässt Du einfach beiseite, und was Dir gefällt, das behältst Du. Je mehr ‘Fehler’ Du Dir erlaubst, desto schneller lernst Du und entdeckst dabei Deinen persönlichen Coding Sound.

2. Verwende die FX

Sagen wir mal, Du beherrschst die Grundlagen, wie man in Sonic Pi Sounds mit sampleund play macht. Was jetzt? Hast Du gewusst, dass Sonic Pi über 27 Studio-FX unterstützt, mit denen Du den Sound deines Codes verändern kannst? FX sind sowas wie raffinierte Bildeffekte in Bildbearbeitungsprogrammen; nur das sie nicht unscharf oder schwarz/weiß machen, sondern dem Sound Hall, Verzerrung oder Echo hinzufügen. Stell’ Dir vor, wie Du das Kabel von Deiner Gitarre ins Effekt-Pedal steckst und dann in den Verstärker. Glücklicherweise braucht man keine Kabel, und Sonic Pi macht es sehr einfach, FX einzusetzen. Du musst nur auswählen, auf welchen Teil Deines Codes Du einen FX anwenden willst und diesen Teil mit dem FX-Code umschließen. Sehen wir uns ein Beispiel an:

sample :loop_garzul
16.times do
  sample :bd_haus
  sleep 0.5
end

Wenn Du einen FX auf das :loop_gazul-Sample anwenden möchtest, steckst du es einfach in einen with_fx-Block, und zwar so:

with_fx :flanger do
  sample :loop_garzul
end
16.times do
  sample :bd_haus
  sleep 0.5
end

Wenn Du jetzt einen FX zur Bassdrum hinzufügen möchtest, dann packe diese auch in ein with_fx ein:

with_fx :flanger do
  sample :loop_garzul
end
with_fx :echo do
  16.times do
    sample :bd_haus
    sleep 0.5
  end
end

Denk’ dran, Du kannst jeden Code mit einem with_fxumgeben und jeder Sound, der ausgegeben wird, geht durch diesen FX.

3. Parametrisiere Deine Synths

Um Deine codierten Klänge so richtig entdecken zu können, wirst Du sicher bald wissen wollen, wie Du Synths und FX steuern kannst. Vielleicht möchtest Du die Dauer eines Tons verändern, mehr Hall hinzufügen oder die Zeit zwischen zwei Echos verändern. Mit optionalen Parametern oder kurz Opts bietet Dir Sonic Pi viele Möglichkeiten, genau das zu tun. Schauen wir uns das mal kurz an. Kopiere diesen Code in einen Puffer und klicke auf Ausführen:

sample :guit_em9

Oh, was für ein wunderbarer Gitarren-Sound! Spielen wir ein bisschen damit. Wie wäre es damit, die Abspielgeschwindigkeit (rate) zu ändern?

sample :guit_em9, rate: 0.5

Was bedeutet der Schnipsel rate: 0.5, den ich hier am Ende hinzugefügt habe? Das ist ein Parameter. Alle Synths und FX in Sonic Pi unterstützen diesen und man kann viel damit anstellen. Versuche mal das hier:

with_fx :flanger, feedback: 0.6 do
  sample :guit_em9
end

Jetzt setze das feedback auf 1 und hör’ Dir die verrückten Sounds an! Einzelheiten zu den vielen Opts, die Dir zur Verfügung stehen, findest Du in der Dokumentation.

5. Live Code

Die beste Art Sonic Pi schnell kennenzulernen, ist, live zu coden. Du fängst mit irgendeinem Codeschnipsel an und veränderst und verbesserst, während der Code abgespielt wird. Wenn Du zum Beispiel nicht weißt, was der Cutoff-Parameter mit einem Sample macht, probiere es einfach aus. Fangen wir mal an! Kopiere diesen Code in einen Puffer von Sonic Pi:

live_loop :experiment do
  sample :loop_amen, cutoff: 70
  sleep 1.75
end

Jetzt klicke auf ausführen und Du wirst einen leicht muffigen Drum-Break hören. Ändere den cutoff:-Wert auf 80 und klicke wieder ausführen. Hörst Du den Unterschied? Versuche es mit 90, 100, 110

Wenn Du die live_loops einmal im Griff hast, willst du nichts mehr anders verwenden. Wann immer ich einen Live-Coding-Gig habe, brauche ich die live_loop wie ein Schlagzeuger seine Sticks. Um mehr über Live-Coding zu erfahren, sieh Dir den Abschnitt 9 im Tutorial an.

5. Mit dem Zufall spielen

Manchmal mogele ich, indem ich Sonic Pi für mich komponieren lasse. Mit Randomisierung lässt sich das großartig hinbekommen; das klingt vielleicht ein wenig kompliziert, ist es aber gar nicht. Sehen wir uns das an. Kopiere diesen Code in einen Puffer:

live_loop :rand_surfer do
  use_synth :dsaw
  notes = (scale :e2, :minor_pentatonic, num_octaves: 2)
  16.times do
    play notes.choose, release: 0.1, cutoff: rrand(70, 120)
    sleep 0.125
  end
end

Wenn Du das laufen lässt, wirst Du eine regelmäßige Reihenfolge von zufälligen Tönen der Skala :e2 :minor_pentatonic gespielt vom :dsaw-Synth hören. “Halt mal, das ist doch keine Melodie”, höre ich Dich schon sagen. Okay, dies ist der erste Teil des Zaubertricks. Jedes Mal, wenn die live_loop durchgelaufen ist, können wir Sonic Pi sagen, es soll die Zufallsfolge an einen bestimmten Punkt zurücksetzen. Das ist ein bisschen so, als würden wir wie Dr. Who in seiner Zeitmaschine TARDIS an einen bestimmten Ort und zu einer bestimmten Zeit zurückkehren. Versuche es mal und schreibe die Zeile use_random_seed 1 in die live_loop:

live_loop :rand_surfer do
  use_random_seed 1
  use_synth :dsaw
  notes = (scale :e2, :minor_pentatonic, num_octaves: 2)
  16.times do
    play notes.choose, release: 0.1, cutoff: rrand(70, 120)
    sleep 0.125
  end
end

Jedes Mal, wenn die live_loopsich wiederholt, wird der Zufalls-Generator zurückgesetzt. Das bedeutet, es werden jedes mal die selben 16 Noten ausgewählt. Hey presto, eine Melodie! Doch jetzt kommt der richtig spannende Teil: Ändere den Wert von seed von 1 in irgendeine andere Zahl. Sagen wir z.B. 4923. Wow! Eine andere Melodie! Also nur durch das Ändern einer Zahl (dem so genannten random seed), kannst Du jegliche nur vorstellbare melodische Kombinationen erforschen! Na, wenn das nicht die Magie des Codes ist.


- Live-Coding

Die Laserstrahlen schnitten durch die Rauschwaden als der Subwoofer den Bass tief in die Körper der Menge pumpte. Die Atmosphäre war erfüllt von einem berauschenden Mix aus Synthesizern und Tanzen. Aber irgendetwas stimmte nicht in diesem Nachtklub. Über der DJ-Kabine leuchtete in bunten Farben futuristischer Text; bewegte sich, tanzte, blinkte. Dies war keine abgefahrene Lichtshow, sondern einfach eine Projektion von Sonic Pi, das auf einem Raspberry Pi lief. Der Insasse der DJ-Kabine drehte keine Platten, nein, er schrieb und bearbeitete Programmcode. Live. Das ist Live-Coding.

Sam Aaron Live-Coding

Es mag sich wie eine weit hergeholte Geschichte aus einem futuristischen Nachtklub anhört, aber Musik auf diese Weise zu programmieren, ist ein wachsender Trend, bekannt als Live-Coding (http://toplap.org). Eine aktuelle Richtung, die diese Art des Musikmachens eingeschlagen hat, ist der Algorave (http://algorave.com) - Veranstaltungen, auf denen Künstler wie ich Musik zum Tanzen programmieren. Du musst aber zum Live-Coden nicht in einem Nachtklub sein. Mit Sonic Pi v2.6+ kannst du überall loslegen, wohin du deinen Raspberry Pi und ein Paar Kopfhörer oder Lautsprecher mitnimmst. Wenn du das Ende dieses Artikels erreicht hast, wirst du bereits deine eigenen Beats programmieren und live bearbeitest. Wohin do von dort weiter gehst, wird einzig von deiner Vorstellungskraft beschränkt.

Live-Loop

Der Schlüssel zum ‘live-coding’ mit Sonic Pi ist das Beherrschen des ‘live_loops’. Schauen wir uns einen an:

live_loop :beats do
  sample :bd_haus
  sleep 0.5
end

Ein Live-Loop hat 4 Hauptbestandteile. Der erste ist sein Name. Unser live_loop oben heißt live_loop. Du kannst frei entscheiden, wie Du Deinen live_loop nennen möchtest. Sei kreativ. Ich benutze oft Namen, die dem Publikum etwas über die Musik mitteilen, die ich mache. Der zweite Bestandteil ist das Wort do, welches anzeigt, wo der live_loop beginnt. Der dritte ist das Wort end, das markiert, wo der live_loop endet. Schließlich gibt es noch den Block innerhalb des live_loop, der beschreibt, was die Schleife wiederholen soll – das ist der Teil zwischen do und end. In unsrem Fall spielen wir ein Bass-Drum-Sample und warten einen halben Takt. Dies führt zu einem schönen regelmäßigen Bass Beat. Auf gehts, kopiere den live_loop in einem leeren Sonic Pi-Puffer und drücke auf Ausführen. Boom, Boom, Boom!.

Zur Laufzeit neu definieren

OK, aber was ist nun das besondere an einem live_loop? Bisher scheint er nur eine überbewertete “Schleife” zu sein! Nun, das Schöne am live_loop ist, dass du ihn im laufenden Programm neu definieren kannst. Das bedeutet, du kannst ändern was er machen soll, während er läuft. Das ist das Geheimnis hinter Live-Coding mit Sonic Pi. Lass uns das ausprobieren:

live_loop :choral_drone do
  sample :ambi_choir, rate: 0.4
  sleep 1
end

Drücke den ‘Ausführen’-Button oder ‘Alt-R’. Du hörst jetzt einen wunderschönen Chor-Sound. Jetzt, während dieser läuft, änderst du ‘rate’-Wert von ‘0,4’ auf ‘0,38’. Drücke erneut ‘Ausführen’. Woah! Hörst du wie sich die Tonhöhe des Chors ändert? Ändere den Wert wieder zurück auf ‘0,4’. Nun setze den Wert auf ‘0,2’, runter bis ‘0,19’ und dann wieder auf ‘0,4’. Siehst du, wie du durch das Ändern eines Parameters, im laufenden Programm, die volle Kontrolle über die Musik erlangen kannst? Spiele ein bisschen mit den Werten für ‘rate’ - wähle deine eigenen Werte. Versuche negative Zahlen, wirklich kleine Zahlen und ganz große Zahlen. Viel Spass!

Schlafen ist wichtig

Eine der wichtigsten Lektionen über ‘live_loop’s ist, dass sie Pausen brauchen. Betrachte einmal folgenden ‘live_loop’:

live_loop :infinite_impossibilities do
  sample :ambi_choir
end

Wenn du versuchst, diese Code auszuführen, wirst du bemerken, dass Sonic Pi sich beschwert, dass der live_loop nicht geschlafen hat. Das ist ein Sicherheitssystem! Nimm dir etwas Zeit und denk darüber nach, was dieser Code vom Computer verlangt. Genau, der Computer wird gefragt eine unendliche Anzahl an Samples zum Nullzeitpunkt zu spielen. Ohne das Sicherheitssystem wird der arme Computer das probieren und währenddessen abstürzen. Also immer daran denken: Deine live_loops müssen ein sleep beinhalten.

Töne kombinieren

Musik ist voll von Dingen, die zur selben Zeit geschehen. Das Schlagzeug spielt zur selben Zeit wie der Bass, Gesang und die Gitarre… In der Informatik nennen wir das Nebenläufigkeit (concurrency). Sonic Pi bietet uns eine einfache Möglichkeit verschiedenste Dinge zur selben Zeit abspielen zu lassen. Benutze einfach mehr als einen live_loop!

live_loop :beats do
  sample :bd_tek
  with_fx :echo, phase: 0.125, mix: 0.4 do
    sample  :drum_cymbal_soft, sustain: 0, release: 0.1
    sleep 0.5
  end
end
live_loop :bass do
  use_synth :tb303
  synth :tb303, note: :e1, release: 4, cutoff: 120, cutoff_attack: 1
  sleep 4
end

Hier haben wir zwei live_loops. Der eine wiederholt schnell Beats, während der andere durch langsames Wiederholen einen verrückten Bass Sound kreiert.

Interessant beim Einsatz mehrerer live_loops ist, dass jeder seine eigene Zeit verwaltet. Das bedeutet, dass man interessante polyrhythmische Strukturen erzeugen kann und sogar mit Phasenverschiebungen á la Steve Reich spielen kann. Schau dir das an:

# Steve Reich's Piano Phase
notes = (ring :E4, :Fs4, :B4, :Cs5, :D5, :Fs4, :E4, :Cs5, :B4, :Fs4, :D5, :Cs5)
live_loop :slow do
  play notes.tick, release: 0.1
  sleep 0.3
end
live_loop :faster do
  play notes.tick, release: 0.1
  sleep 0.295
end

Alles zusammenführen

Jedes dieser Tutorials werden wir mit einem abschließenden Code-Beispiel beenden, das sich aller zuvor behandelter Ideen bedient. Schau Dir diesen Code genau an und überlege Dir, was er tut. Kopiere ihn anschließend in einen neuen Sonic Pi-Puffer, drücke auf Start und hör Dir an, wie er klingt. Ändere zum Schluss etwas am Code, indem Du Parameter veränderst oder Dinge auskommentierst. Vielleicht kannst Du das als Ausgangspunkt für eine eigene Performance nutzen. Das Wichtigste ist auf jeden Fall der Spaß dabei. Bis zum nächsten Mal …

with_fx :reverb, room: 1 do
  live_loop :time do
    synth :prophet, release: 8, note: :e1, cutoff: 90, amp: 3
    sleep 8
  end
end
live_loop :machine do
  sample :loop_garzul, rate: 0.5, finish: 0.25
  sample :loop_industrial, beat_stretch: 4, amp: 1
  sleep 4
end
live_loop :kik do
  sample :bd_haus, amp: 2
  sleep 0.5
end
with_fx :echo do
  live_loop :vortex do
    # use_random_seed 800
    notes = (scale :e3, :minor_pentatonic, num_octaves: 3)
    16.times do
      play notes.choose, release: 0.1, amp: 1.5
      sleep 0.125
    end
  end
end

- Kodierte Beats

Eine der spannendsten und einflussreichsten technischen Entwicklungen der modernen Musik ist die Erfindung von Samplern. Sampler sind Musikinstrumente, die es einem erlauben Töne aufzunehmen, sie zu verändern und auf verschiedene Arten und Weisen wiederzugeben. Zum Beispiel ist es mit einem Sampler möglich ein Schlagzeug Solo (oder Break) von einer alten Schalplatte einzuspielen und es anschließend als Grundlage für einen neuen Beat zu verwenden, indem man es mit halber Geschwindigkeit wiedergibt. So ist früher Hip-Hop entstanden und heute gibt es kaum noch Elektronische Musik, die ohne irgendeine Art von Samples auskommt. Samples bieten dir eine großartige Möglichkeit auf einfache Art und Weise neue und interessante Elemente in Deine live gecodete Performance einfließen zu lassen.

Aber wo kriegen wir einen Sampler her? Wir haben bereits einen - es ist unser Raspberry Pi! Die mitgelieferte Live-Coding App Sonic Pi stellt uns einen mächtigen Sampler bereit. Lasst ihn uns ausprobieren!

Der Amen Break

Eines der klassischen Schlagzeug Break Samples mit dem größten Wiedererkennungswert ist der Amen Break. Er wurde erstmals 1969 von den Winstons in ihrem Song “Amen Brother” als Teil des Drum Break aufgeführt. Doch erst die Wiederentdeckung durch frühe Hip-hop Musiker der 80er und der Einsatz in Samplern bewirkte den Einzug des Samples in einer Bandbreite von Musikstilen, wie Drum and Bass, Breakbeat, Hardcore Techno und Breakcore.

Ich bin mir sicher, Du freust Dich zu hören, dass das Sample direkt in Sonic Pi eingebaut ist. Bereite einfach einen Puffer vor und kopiere den folgenden Code hinein:

sample :loop_amen

Drücke auf Start und boom! Du hörst dir gerade eines der einflussreichsten Drum Breaks in der Geschichte der Tanzmusik an. Dieses Sample ist allerdings nicht damit berühmt geworden, einmal abgespielt zu werden. Vielmehr ist es wie gemacht dafür wiederholt zu werden.

Beat Stretching

Lass uns den Amen Break in Schleife schalten, indem wir unseren alten Bekannten, denlive_loop aus dem Tutorial vom letzten Monat, einsetzen:

live_loop :amen_break do
  sample :loop_amen
  sleep 2
end

OK, es wiederholt sich. Allerdings kommt es am Ende eines Durchlaufs zu einer lästige Pause. Diese entsteht durch unserer Anweisung 2 Takte zu pausieren. Das :loop_amen Sample dauert bei einem voreingestellten BPM Wert von 60 nur 1.753 Takte. Das bedeutet kommt es am Sample Ende zu einer Pause von 2 - 1.753 = 0.247 Takten, was kurz, aber durchaus wahrnehmbar ist.

Um dieses Problem zu beheben, können wir die beat_stretch: Option verwenden. Sie sagt Sonic Pi, dass das Sample auf die angegebene Anzahl von Takten ausgedehnt (bzw. gestaucht) werden soll.

Die Sonic Pi Funktionen sample und synth bieten uns über optionale Parameter wie amp:, cutoff: und release: viele zusätzliche Kontrollmöglichkeiten. Da die Bezeichnung optimale Parameter allerdings ganz schön lang ist, werden wir der Einfachheit halber ab jetzt von opts sprechen.

live_loop :amen_break do
  sample :loop_amen, beat_stretch: 2
  sleep 2
end  

Jetzt schwingen wir das Tanzbein! Vielleicht wollen wir es noch etwas schneller, oder doch einen Ton gemächlicher - je nach Stimmung.

Mit der Zeit spielen

OK, wie sieht es aus, wenn wir den Stiel unserer Musik zu Hip Hop oder Breakcore ändern wollen? Eine einfache Möglichkeit das zu tun ist mit der Zeit zu spielen - oder in anderen Worten am Tempo herumbasteln. In Sonic Pi ist das super leicht - füge einfach use_bpm in deinen Live-Loop ein:

live_loop :amen_break do
  use_bpm 30
  sample :loop_amen, beat_stretch: 2
  sleep 2
end 

Während du gerade zu diesen langsamen Beats rappst, beobachte, dass obwohl wir immer noch eine Pause von 2 machen und unsere BPM bei 30 liegen nichts verzögert klingt. Die beat_stretch Option berücksichtigt den aktuellen BPM Wert und bringt alles in Einklang.

Jetzt kommen wir zum spannenden Teil. Während der Loop läuft, verändere den Wert 30 in der use_bpm 30 Zeile zu 50. Wuhuu, auf einmal ist alles ohne aus dem Einklang zu geraten schneller geworden! Versuch das Tempo noch etwas mehr zu erhöhen - 80, 120 und um es wirklich verrückt klingen zu lassen, trage 200 ein!

Filtern

Jetzt können wir damit beginnen Samples in unseren Live-Loop zu integrieren. Lass uns einige der lustigsten Optionen des sample Synths anschauen. Zunächst hätten wir cutoff:, das den Cut-Off-Filter des Samplers kontrolliert. Per default ist dieser ausgestellt. Aber du kannst ihn ganz einfach frei geben:

live_loop :amen_break do
  use_bpm 50
  sample :loop_amen, beat_stretch: 2, cutoff: 70
  sleep 2
end  

Nun, ändere den Wert der cutoff: Option. Erhöhe ihn zum Beispiel auf 100, drücke auf Start und warte bis der Loop einmal durchgelaufen ist, um die Änderung zu hören. Du kannst beobachten, dass niedrige Werte wie 50 den Klang voll und basslastig machen, hohe Werte wie 100 und 200 aber voll und kratzend klingen. Das liegt daran, dass die cutoff: Option die Höhen wegschneidet - genau so wie ein Rasenmäher die Enden von Grashalmen abschneidet. Die cutoff: fungiert als eine Längeneinstellung. Sie legt fest, wie viel Grass nach dem Mähen übrig bleibt.

Slicing

Ein anderes tolles Tool, das wir ausprobieren können, ist der FX Slicer. Er stückelt unseren Sound in Einzelsequenzen. Verpacke die sample Zeile dafür einfach mit dem folgenden FX Code:

live_loop :amen_break do
  use_bpm 50
  with_fx :slicer, phase: 0.25, wave: 0, mix: 1 do
    sample :loop_amen, beat_stretch: 2, cutoff: 100
  end
  sleep 2
end

Beobachte, wie der Klang dadurch etwas mehr auf und ab federt. (Du kannst dir den ursprünglichen Klang ohne FX anhören, indem du die Option mix: auf 0 setzt.) Als nächstes, versuche dich an der phase: Option. Das ist die Rate (in Beats) in der gestückelt wird. Ein kleiner Wert wie 0.125 stückelt häufig, hohe Werte wie 0.5 stückeln hingegen langsamer. Beobachte, dass eine stufenweises Halbieren oder Vierteln der phase: tendenziell immer gut klingt. Setzte zuletzt die wave: Option auf 0, 1, oder 2 und höre dir an, wie die Änderung klingt. Die Werte stehen für unterschiedliche Schwingungsformen. 0 repräsentiert eine Sägezahnschwindung (hard in, fade out), 1 eine Rechteckschwingung (hard in, hard out) und 2 eine Dreieckschwingung (hard in, hard out).

Alles zusammenführen

Lass uns für unser letztes Beispiel einem Blick auf die frühe Drum and Bass Szene in Bristol werfen. Mach dir keinen Sorgen, wenn du nicht genau verstehst, wie das Beispiel funktioniert. Füge den Code einfach in Sonic Pi ein, klicke auf Ausführen und versuche dich am Live-Coden, indem du die Werte der verschiedenen Optionen veränderst. Vergesse nicht deine Kreationen mit anderen zu teilen! Bis zum nächsten Mal…

use_bpm 100
live_loop :amen_break do
  p = [0.125, 0.25, 0.5].choose
  with_fx :slicer, phase: p, wave: 0, mix: rrand(0.7, 1) do
    r = [1, 1, 1, -1].choose
    sample :loop_amen, beat_stretch: 2, rate: r, amp: 2
  end
  sleep 2
end
live_loop :bass_drum do
  sample :bd_haus, cutoff: 70, amp: 1.5
  sleep 0.5
end
live_loop :landing do
  bass_line = (knit :e1, 3, [:c1, :c2].choose, 1)
  with_fx :slicer, phase: [0.25, 0.5].choose, invert_wave: 1, wave: 0 do
    s = synth :square, note: bass_line.tick, sustain: 4, cutoff: 60
    control s, cutoff_slide: 4, cutoff: 120
  end
  sleep 4
end

- Synth Riffs

Ob polterndere Oszillatoren oder die verstimmten Klänge von Sägezahnschwingungen, die sich durch ein Stück ziehen, der Lead Synth - der melodische Hauptpart eines Stücks - spielt in jeder elektronischen Komposition eine wichtige Rolle. Im der letzten Einheit dieses Tutorials haben wir gelernt, wie man Klänge erzeugt. Jetzt werden wir uns damit beschäftigen, wie wir die drei Hauptkomponenten eines Synth Riffs - Klangfarbe, Melodie und Rhythmus - coden können.

OK, fahre deinen Raspberry Pi hoch, öffne Sonic Pi v2.6+ und auf gehts, lass und Musik machen!

Die Welt der Klangfarben

Ein essentieller Teil jedes Synth Riffs ist das Verändern und Experimentieren mit der Klangfarbe. Sonic Pi bietet uns zwei Möglichkeiten, die Klangfarbe zu kontrollieren - für dramatische Änderungen können wir verschiedene Synths verwenden, für dezente Umwandlungen reicht es die Optionen eines Synths einzustellen. Wir könnten auch FX verwenden, aber das heben wir uns für ein anderes Tutorial auf…

Lass uns einen einfachen Live-Loop schreiben, in dem wir laufend den Synth ändern:

live_loop :timbre do
  use_synth (ring :tb303, :blade, :prophet, :saw, :beep, :tri).tick
  play :e2, attack: 0, release: 0.5, cutoff: 100
  sleep 0.5
end

Schau dir den Code genauer an. Mit dem tick Befehl gehen wir Eintrag für Eintrag durch einen Ring von Synth Namen (wobei wir die Liste immer wieder wiederholen). Anschließend übergeben wir diesen Synth an die use_synth Funktion, die den aktuellen Synth unseres Live-Loops ändert. Außerdem spielen wir die Note :e2 (e der zweiten Oktave) mit einer Abklingzeit von 0.5 Takten (0.5 Sekunden bei unser Standard-BPM von 60) und einem cutoff: Wert von 100.

Hörst du, wie die unterschiedlichen Synths vollkommen verschiedene Klänge erzeugen, obwohl sie alle die selbe Note spielen? Lass uns damit experimentieren. Erhöhe oder verkleinere den Wert der Abklingzeit. Ändere zum Beispiel die Werte der Optionen attack: und release:, um zu sehen, wie sich unterschiedliche Fade-In und Fade-Out Zeiten auf den Klang auswirken. Zuletzt kannst du den Wert der cutoff: Option ändern, um zu beobachten, wie unterschiedliche Cut-Off-Wert die Klangfarbe beeinflussen (Werte zwischen 60 und 130 sind gut). Probiert mal aus, wie viele verschiedene Klänge du erzeugen kannst, indem du an diesen wenigen Parametern rumschraubst. Wenn dir das gelingt, kannst du im dir im Help System den Eintrag zu Synth anschauen. Hier findest du eine Auflistung aller Synths und der Optionen, die sie bereitstellen. Ein Reich an Möglichkeiten liegt dir zu Füßen.

Klangfarbe

Klangfarbe (engl. timbre) ist nur ein ausgefallenes Wort für den Klang eines Geräusches. Wenn man die selbe Note auf verschiedenen Instrumenten, wie zum Beispiel einer Geige, einer Gitarre oder einem Klavier spielt, so bleibt die Tonhöhe (wie hoch oder niedrig ein Ton ist) immer die selbe. Die Tonqualität hingegen unterscheidet sich. Dieser Unterschied, der einem erlaubt festzustellen, ob es sich um ein Klavier oder eine Gitarre handelt, ist die Klangfarbe.

Melodische Komposition

Ein anderer wichtiger Aspekt in der Zusammenstellung unseres Leas Synths ist die Wahl der Noten, die gespielt werden sollen. Wenn du bereits eine Idee dafür hast, kannst du einfach einen Ring erstellen, der über die gewünschte Notenfolge iteriert:

live_loop :riff do
  use_synth :prophet
  riff = (ring :e3, :e3, :r, :g3, :r, :r, :r, :a3)
  play riff.tick, release: 0.5, cutoff: 80
  sleep 0.25
end

Hier haben wir unsere Melodie mit Hilfe eines Rings definiert, der sich aus Noten wie :e3 und Pausen - dargestellt durch :r- zusammensetzt. Wir nutzen .tick um über die Notenfolge zu iterieren und so einen sich wiederholenden Riff zu erzeugen.

Automatische Melodie

Es ist nicht leicht einen gut klingenden Riff aus dem Nichts zu zaubern. Statt dessen ist es oft hilfreich sich von Sonic Pi eine Auswahl von zufälligen Riffs ausgeben zu lassen und einen von diesen auszuwählen. Um das zu tun, verbinden wir drei bekannte Konzepte miteinander: Ringe, Randomisierung und Zufallszahlen. Schauen wir uns ein Beispiel an:

live_loop :random_riff do
  use_synth :dsaw
  use_random_seed 3
  notes = (scale :e3, :minor_pentatonic).shuffle
  play notes.tick, release: 0.25, cutoff: 80
  sleep 0.25
end

There’s a few things going on - let’s look at them in turn. First, we specify that we’re using random seed 3. What does this mean? Well, the useful thing is that when we set the seed, we can predict what the next random value is going to be - it’s the same as it was last time we set the seed to 3! Another useful thing to know is that shuffling a ring of notes works in the same way. In the example above we’re essentially asking for the ‘third shuffle’ in the standard list of shuffles - which is also the same every time as we’re always setting the random seed to the same value right before the shuffle. Finally we’re just ticking through our shuffled notes to play the riff.

Now, here’s where the fun starts. If we change the random seed value to another number, say 3000, we get an entirely different shuffling of the notes. So now it’s extremely easy to explore new melodies. Simply choose the list of notes we want to shuffle (scales are a great starting point) and then choose the seed we want to shuffle with. If we don’t like the melody, just change one of those two things and try again. Repeat until you like what you hear!

Pseudo-Randomisierung

Sonic Pi’s randomisation is not actually random it’s what’s called pseudo random. Imagine if you were to roll a dice 100 times and write down the result of each roll onto a piece of paper. Sonic Pi has the equivalent of this list of results which it uses when you ask for a random value. Instead of rolling an actual dice, it just picks the next value from the list. Setting the random seed is just jumping to a specific point in that list.

Finde deinen Rythmus

Ein weiteres wichtiges Merkmal unseres Riffs ist der Rhythmus, d.h. wann wir eine Note spielen und wann nicht. Wir haben bereits gesehen, dass wir :r nutzen können, um Pausen in einen Ring einzufügen. Eine weiter Möglichkeit auf den Rhythmus Einfluss zu nehmen sind sog. Spreads, über die wir in einem kommenden Tutorial mehr lernen werden. Heute nutzen wir Randomisierung, um unseren Rhythmus zu finden. Anstatt immer jede Note in einem Ring zu spielen, können wir über eine Bedingung festlegen, mit welcher Wahrscheinlichkeit sie abgespielt werden. Lass uns einen Blick darauf werfen:

live_loop :random_riff do
  use_synth :dsaw
  use_random_seed 30
  notes = (scale :e3, :minor_pentatonic).shuffle
  16.times do
    play notes.tick, release: 0.2, cutoff: 90 if one_in(2)
    sleep 0.125
  end
end

Eine nützliche Funktion in diesem Zusammenhang ist die Funktion one_in, die true bzw. false mit einer bestimmten Wahrscheinlichkeit zurückgibt. Hier verwenden wir den Wert 2, d.h. one_in gibt durchschnittlich ein Mal alle zwei Aufrufe true zurück. Mit anderen Worten, true wird in 50% der Fälle zurückgegeben. Höhere Werte bewirken, dass statt true häufiger false zurückgegeben wird. Das führt zu mehr Lücken in unserem Riff.

Beobachte, dass wir mit dem Befehl 16.times Wiederholung eingebaut haben. Das haben wir gemacht, damit sich unser Zufallsgenerator (der Rückgabewert der one_in Funktion) nur alle 16 Noten zurücksetzt und unser Rhythmus sich so alle 16 Schlägen wiederholt. Wir nehmen damit keinen Einfluss auf das durcheinander Mischen, weil letzteres direkt nach dem Initiieren des Zufallsgenerators passiert. Wir können über die Größe der Wiederholungen die Länge unseres Riffs verändern. Versuche mal die Zahl 16 auf 8 oder sogar 4 oder 3 zu ändern und schaue dir an, wie sich das auf den Rhythmus des Riffs auswirkt.

Alles zusammenführen

OK, lass uns zum Schluss alles, das wir gelernt haben, in einem abschließenden Beispiel nutzen. Bis zum nächsten Mal!

live_loop :random_riff do
  #  uncomment to bring in:
  #  synth :blade, note: :e4, release: 4, cutoff: 100, amp: 1.5
  use_synth :dsaw
  use_random_seed 43
  notes = (scale :e3, :minor_pentatonic, num_octaves: 2).shuffle.take(8)
  8.times do
    play notes.tick, release: rand(0.5), cutoff: rrand(60, 130) if one_in(2)
    sleep 0.125
  end
end
 
live_loop :drums do
  use_random_seed 500
  16.times do
    sample :bd_haus, rate: 2, cutoff: 110 if rand < 0.35
    sleep 0.125
  end
end
 
live_loop :bd do
  sample :bd_haus, cutoff: 100, amp: 3
  sleep 0.5
end

- Acid Bass

Wenn man sich die Geschichte der Elektronischen Tanzmusik anschaut, ist es schier unmöglich den enormen Einfluss, den der winzige Synthesizer Roland TB-303 hatte, zu übersehen. Er ist das Geheimnis hinter dem Klang des originalen Acid Bass. Diese klassisch quietschenden und quieksenden Bass Riffe des TB-303 kann man sowohl in der frühen Chicago House Szene als auch bei Interpreten moderner Elektronischen Musik wie Plastikman, Squarepusher und Aphex Twin hören.

Interessanterweise hatte Roland nicht vorgesehen, dass der TB-303 für Tanzmusik zum Einsatz kommt. Er wurde ursprünglich als eine Übungshilfe für Gitarristen entwickelt. Die Firma hatte sich überlegt, dass Leute mit Hilfe des Synthesizers Basslinien programmieren würden zu denen sie jammen könnten. Leider gab es damit mehrere Probleme: die Programmierung war etwas zu kompliziert, der Klang glich nicht wirklich einer Bassgitarre und sie waren teuer in der Anschaffung. Um ihre Verluste zu minimieren, brach Roland die Produktion nach dem Verkauf von 10.000 Exemplaren ab. Nach ein paar Jahren des Daseins als Staubfänger in den Regalen der Gitarristen wanderten die meisten TB-303s in die Schaufenster von Second-Hand-Läden. Dort warteten sie auf ihre Wiederentdeckung durch eine neue Generation von Experimentierfreudigen, die anfingen sie auf eine Art und Weise zu nutzten, die Roland nie vorgesehen hatte, um abgefahrene Klänge zu erzeugen. Das war die Geburtsstunde des Acid House.

Obwohl es nicht leicht ist sich einen originalen TB-303 zu beschaffen, wirst Du Dich sicher freuen zu hören, dass Du Deinen Raspberry Pi mit Hilfe von Sonic Pi in einen TB-303 verwandeln kannst. Los gehts, wirf Sonic Pi an, kopiere den folgenden Code in einen leeren Puffer und klicke auf Ausführen:

use_synth :tb303
play :e1

Siehe da! Wir haben einen Acid Bass! Lass uns weiter experimentieren…

Bringe den Bass zum Glucksen

Lass uns zunächst einen live Arpeggiator bauen. In unserer letzten Tutorialeinheit haben wir gelernt, dass Riffs durch Ringe von Noten, die wir nach einander in einer sich wiederholenden Schleife wiedergeben, repräsentiert werden können. Lass uns einen Live-Loop schreiben, der das gleiche macht:

use_synth :tb303
live_loop :squelch do
  n = (ring :e1, :e2, :e3).tick
  play n, release: 0.125, cutoff: 100, res: 0.8, wave: 0
  sleep 0.125
end

Schaue dir jede Codeziele genau an.

In der ersten Zeile setzen wir den Defaultwert deruse_synth Funktion auf tb303. In der zweiten Zeile erstellen wir einen Live-Loop vom Typen :squelch, der sich die ganze Zeit wiederholt. In der dritten Zeile erstellen wir unseren Riff - einen Ring aus Noten (E in den Oktaven 1, 2, und 3), der diese mit Hilfe eines .ticks in Schleife durchläuft. Wir definieren n als die aktuelle Note in unserem Riff. Das Gleichheitszeichen hier bedeutet, dass wir den Wert auf der rechten Seite der Bezeichnung auf der linken Seite zuzuweisen. In jedem Durchlauf unseres Loops hat nalso ein anderer Wert. Im ersten Durchlauf wird n auf :e1 gesetzt. Im zweiten Durchlauf wird es auf :e2, gefolgt von :e3 und dann wieder auf :e1 usw. gesetzt. Mit Zeile viert wird der eigentliche :tb303 Synth eingeleitet. Dabei werden einige interessante Optionen mitgegeben: release:, cutoff:, res: and wave: über die wir etwas später genauer sprechen werden. In Zeile fünf machen wir eine Pause - wir geben unserem Live-Loop vor sich alle 0.125s (oder 8 mal pro Sekunde bei einem BPM von 60) zu wiederholen. Zeile sechs markiert das Ende (end) des Live-Loops. Sie teilt Sonic Pi nur mit, wo die zu wiederholende Sequenz zu Ende ist.

Während du dir noch überlegst, wie das alles genau funktioniert, tippe den oben stehenden Code ab und klicke auf Ausführen. Du solltest den :tb303 lostreten hören. Jetzt legen wir richtig los: lass uns mit dem Live-Coding beginnen.

Whilst the loop is still live, change the cutoff: opt to 110. Now hit the Run button again. You should hear the sound become a little harsher and more squelchy. Dial in 120 and hit run. Now 130. Listen how higher cutoff values make it sound more piercing and intense. Finally, drop it down to 80 when you feel like a rest. Then repeat as many times as you want. Don’t worry, I’ll still be here…

Another opt worth playing with is res:. This controls the level of resonance of the filter. A high resonance is characteristic of acid bass sounds. We currently have our res: set to 0.8. Try cranking it up to 0.85, then 0.9, and finally 0.95. You might find that a cutoff such as 110 or higher will make the differences easier to hear. Finally go crazy and dial in 0.999 for some insane sounds. At a res this high, you’re hearing the cutoff filter resonate so much it starts to make sounds of its own!

Ändere zum Schluss die wave: Option auf 1, um einen großen Einfluss auf die Klangfarbe zu nehmen. Dies legt die Art der Schwingungserzeugung fest. Der Standard ist mit 0 eine Sägezahnschwingung. 1 steht für eine Pulswelle und 2für eine Dreiecksschwingung.

Versuche natürlich auch verschiedene Riffs zu erzeugen, indem du die Noten im Ring änderst oder sogar Noten aus Skalen oder Akkorden auswählst. Viel Spaß mit deinem ersten Acid Bass Synth.

Den TB-303 zerlegen

Der Aufbau des originalen TB-303 ist eigentlich ganz einfach. Wie man dem folgenden Diagramm entnehmen kann, gibt es nur vier Hauptbestandteile.

TB-303 Design

First is the oscillator wave - the raw ingredients of the sound. In this case we have a square wave. Next there’s the oscillator’s amplitude envelope which controls the amp of the square wave through time. These are accessed in Sonic Pi by the attack:, decay:, sustain: and release: opts along with their level counterparts. For more information read Section 2.4 ‘Duration with Envelopes’ in the built-in tutorial. We then pass our enveloped square wave through a resonant low pass filter. This chops off the higher frequencies as well as having that nice resonance effect. Now this is where the fun starts. The cutoff value of this filter is also controlled by its own envelope! This means we have amazing control over the timbre of the sound by playing with both of these envelopes. Let’s take a look:

use_synth :tb303
with_fx :reverb, room: 1 do
  live_loop :space_scanner do
    play :e1, cutoff: 100, release: 7, attack: 1, cutoff_attack: 4, cutoff_release: 4
    sleep 8
  end
end

Für jede Standard-Hüllkurvenoption gibt es eine entsprechende cutoff_-Option im :tb303-Synthesizer. Um die cutoff-Anschlagszeit zu ändern, können wir die Option cutoff_attack: verwenden. Kopiere den Code unten in einen leeren Puffer und drücke Run. Du wirst einen verrrückten Sound hören, der ein- und auswobbelt. Nun spiel ein bisschen herum. Versuche, die cutoff_attack:-Zeit auf 1 zu ändern, dann auf 0.5. Und jetzt probiere 8.

Beobachte, dass wir, um etwas mehr Stimmung zu erzeugen, alles durch einen :reverb FX schicken - probiere ein paar andere Effekte aus und schaue welche hier gut passen!

Alles zusammenführen

Zum Schluss gibt es ein Beispiel, das ich mit Hilfe der Konzepte aus diesem Tutorial komponiert habe. Kopiere den Code in einen leeren Puffer, höre es Dir ein Weile lang an und dann versuche Deine eigenen Änderungen live zu coden. Überzeuge Dich davon, was für verrückte Klänge Du erzeugen kannst! Bis zum nächste Mal …

use_synth :tb303
use_debug false
 
with_fx :reverb, room: 0.8 do
  live_loop :space_scanner do
    with_fx :slicer, phase: 0.25, amp: 1.5 do
      co = (line 70, 130, steps: 8).tick
      play :e1, cutoff: co, release: 7, attack: 1, cutoff_attack: 4, cutoff_release: 4
      sleep 8
    end
  end
 
  live_loop :squelch do
    use_random_seed 3000
    16.times do
      n = (ring :e1, :e2, :e3).tick
      play n, release: 0.125, cutoff: rrand(70, 130), res: 0.9, wave: 1, amp: 0.8
      sleep 0.125
    end
  end
end

- Musisches Minecraft

Hallo und willkommen zurück! In den zurückliegenden Tutorialeinheiten haben wir uns ausschließlich auf die musikalischen Möglichkeiten von Sonic Pi konzentriert (um unseren Raspberry Pi in ein konzertfähiges Musikinstrument zu verwandeln). Bisher haben wir gelernt wie man:

Live-Coded - Klänge live improvisieren, gigantische Beats komponiert, mächtige Lead Synths generiert, und den bekannten TB-303 Acid Bass nachbaut.

Es gibt noch so viel Dinge, die ich Dir zeigen kann (was ich in zukünftigen Tutorialeinheiten auch machen werden). In diesem Monat werfen wir allerdings einen Blick auf etwas, das Du von Sonic Pi wahrscheinlich nicht erwartet hast: Minecraft kontrollieren.

Hello Minecraft World

OK, lass uns loslegen. Fahre deinen Raspberry Pi hoch, werfe Minecraft Pi an und erstelle eine neue Welt. Starte nun Sonic Pi und verändere die Größe deiner Fenster so, dass du sowohl Sonic Pi als auch Minecraft Pi auf deinem Bildschirm sehen kannst.

Gib Folgendes in einen leeren Puffer ein:

mc_message "Hello Minecraft from Sonic Pi!"

Drücke jetzt auf Ausführen. Boom! Deine Nachricht erscheint in Minecraft! Wie einfach war das denn? Lege nun dieses Tutorial kurz beiseite und spiele mit deinen eigenen Nachrichten herum. Viel Spaß!

Screen 0

Schall-Teleporter

Now let’s do some exploring. The standard option is to reach for the mouse and keyboard and start walking around. That works, but it’s pretty slow and boring. It would be far better if we had some sort of teleport machine. Well, thanks to Sonic Pi, we have one. Try this:

mc_teleport 80, 40, 100

Crikey! That was a long way up. If you weren’t in flying-mode then you would have fallen back down all the way to the ground. If you double-tap space to enter flying-mode and teleport again, you’ll stay hovering at the location you zap to.

Now, what do those numbers mean? We have three numbers which describe the coordinates of where in the world we want to go. We give each number a name - x, y and z:

x - wie weit links und rechts (80 in unserem Beispiel) y - wie hoch wir sein wollen (40 in unserem Beispiel) z - wie weit vorwärts und rückwärts (100 in unserem Beispiel)

By choosing different values for x, y and z we can teleport anywhere in our world. Try it! Choose different numbers and see where you can end up. If the screen goes black it’s because you’ve teleported yourself under the ground or into a mountain. Just choose a higher y value to get back out above land. Keep on exploring until you find somewhere you like…

Using the ideas so far, let’s build a Sonic Teleporter which makes a fun teleport sound whilst it whizzes us across the Minecraft world:

mc_message "Preparing to teleport...."
sample :ambi_lunar_land, rate: -1
sleep 1
mc_message "3"
sleep 1
mc_message "2"
sleep 1
mc_message "1"
sleep 1
mc_teleport 90, 20, 10
mc_message "Whoooosh!"

Screen 0

Magische Blöcke

Now you’ve found a nice spot, let’s start building. You could do what you’re used to and start clicking the mouse furiously to place blocks under the cursor. Or you could use the magic of Sonic Pi. Try this:

x, y, z = mc_location
mc_set_block :melon, x, y + 5, z

Now look up! There’s a melon in the sky! Take a moment to look at the code. What did we do? On line one we grabbed the current location of Steve as the variables x, y and z. These correspond to our coordinates described above. We use these coordinates in the fn mc_set_block which will place the block of your choosing at the specified coordinates. In order to make something higher up in the sky we just need to increase the y value which is why we add 5 to it. Let’s make a long trail of them:

live_loop :melon_trail do
  x, y, z = mc_location
  mc_set_block :melon, x, y-1, z
  sleep 0.125
end

Now, jump over to Minecraft, make sure you’re in flying-mode (double tap space if not) and fly all around the world. Look behind you to see a pretty trail of melon blocks! See what kind of twisty patterns you can make in the sky.

Live Coding Minecraft

Those of you that have been following this tutorial over the last few months will probably have your minds blown at this point. The trail of melons is pretty cool, but the most exciting part of the previous example is that you can use live_loop with Minecraft! For those that don’t know, live_loop is Sonic Pi’s special magic ability that no other programming language has. It lets you run multiple loops at the same time and allows you to change them whilst they run. They are incredibly powerful and amazing fun. I use live_loops to perform music in nightclubs with Sonic Pi - DJs use discs and I use live_loops :-) However, today we’re going to live code both music and Minecraft.

Let’s get started. Run the code above and start making your melon trail again. Now, without stopping the code, just simply change :melon to :brick and hit run. Hey presto, you’re now making a brick trail. How simple was that! Fancy some music to go with it? Easy. Try this:

live_loop :bass_trail do
  tick
  x, y, z = mc_location
  b = (ring :melon, :brick, :glass).look
  mc_set_block b, x, y -1, z
  note = (ring :e1, :e2, :e3).look
  use_synth :tb303
  play note, release: 0.1, cutoff: 70
  sleep 0.125
end

Now, whilst that’s playing start changing the code. Change the block types - try :water, :grass or your favourite block type. Also, try changing the cutoff value from 70 to 80 and then up to 100. Isn’t this fun?

Alles zusammenführen

Screen 0

Let’s combine everything we’ve seen so far with a little extra magic. Let’s combine our teleportation ability with block placing and music to make a Minecraft Music Video. Don’t worry if you don’t understand it all, just type it in and have a play by changing some of the values whilst it’s running live. Have fun and see you next time…

live_loop :note_blocks do
  mc_message "This is Sonic Minecraft"
  with_fx :reverb do
    with_fx :echo, phase: 0.125, reps: 32 do
      tick
      x = (range 30, 90, step: 0.1).look
      y = 20
      z = -10
      mc_teleport x, y, z
      ns = (scale :e3, :minor_pentatonic)
      n = ns.shuffle.choose
      bs = (knit :glass, 3, :sand, 1)
      b = bs.look
      synth :beep, note: n, release: 0.1
      mc_set_block b, x+20, n-60+y, z+10
      mc_set_block b, x+20, n-60+y, z-10
      sleep 0.25
    end
  end
end
live_loop :beats do
  sample :bd_haus, cutoff: 100
  sleep 0.5
end

- Bizet Beats

After our brief excursion to the fantastic world of coding Minecraft with Sonic Pi last month, let’s get musical again. Today we’re going to bring a classical operatic dance piece straight into the 21st century using the awesome power of code.

Outrageous and Disruptive

Let’s jump into a time machine back to the year 1875. A composer called Bizet had just finished his latest opera Carmen. Unfortunately like many exciting and disruptive new pieces of music people initially didn’t like it at all because it was too outrageous and different. Sadly Bizet died ten years before the opera gained huge international success and became one of the most famous and frequently performed operas of all time. In sympathy with this tragedy let’s take one of the main themes from Carmen and convert it to a modern format of music that is also too outrageous and different for most people in our time - live coded music!

Die Habanera dekodieren

Trying to live code the whole opera would be a bit of a challenge for this tutorial, so let’s focus on one of the most famous parts - the bass line to the Habanera:

Habanera Riff

This may look extremely unreadable to you if you haven’t yet studied music notation. However, as programmers we see music notation as just another form of code - only it represents instructions to a musician instead of a computer. We therefore need to figure out a way of decoding it.

Anmerkungen

The notes are arranged from left to right like the words in this magazine but also have different heights. The height on the score represents the pitch of the note. The higher the note on the score, the higher the pitch of the note.

In Sonic Pi we already know how to change the pitch of a note - we either use high or low numbers such as play 75 and play 80 or we use the note names: play :E and play :F. Luckily each of the vertical positions of the musical score represents a specific note name. Take a look at this handy look up table:

Anmerkungen

Pausen

Music scores are an extremely rich and expressive kind of code capable of communicating many things. It therefore shouldn’t come as much of a surprise that musical scores can not only tell you what notes to play but also when not to play notes. In programming this is pretty much equivalent to the idea of nil or null - the absence of a value. In other words not playing a note is like the absence of a note.

If you look closely at the score you’ll see that it’s actually a combination of black dots with lines which represent notes to play and squiggly things which represent the rests. Luckily Sonic Pi has a very handy representation for a rest: :r, so if we run: play :r it actually plays silence! We could also write play :rest, play nil or play false which are all equivalent ways of representing rests.

Rhythmus

Finally, there’s one last thing to learn how to decode in the notation - the timings of the notes. In the original notation you’ll see that the notes are connected with thick lines called beams. The second note has two of these beams which means it lasts for a 16th of a beat. The other notes have a single beam which means they last for an 8th of a beat. The rest has two squiggly beams which means it also represents a 16th of the beat.

When we attempt to decode and explore new things a very handy trick is to make everything as similar as possible to try and see any relationships or patterns. For example, when we re-write our notation purely in 16ths you can see that our notation just turns into a nice sequence of notes and rests.

Habanera Riff 2

Re-coding the Habanera

Wir sind jetzt in der Lage, um diese Basslinie für Sonic Pie zu übersetzen. Lass uns diese Noten kodieren und gut sein lassen:

(ring :d, :r, :r, :a, :f5, :r, :a, :r)

Lass uns schauen, wie es sich anhört. Stelle es in einen Live-Loop und ticke durch:

live_loop :habanera do
  play (ring :d, :r, :r, :a, :f5, :r, :a, :r).tick
  sleep 0.25
end

Fabulous, that instantly recognisable riff springs to life through your speakers. It took a lot of effort to get here, but it was worth it - high five!

Stimmungssynthesizer

Now we have the bass line, let’s re-create some of the ambience of the operatic scene. One synth to try out is :blade which is a moody 80s style synth lead. Let’s try it with the starting note :d passed through a slicer and reverb:

live_loop :habanera do
  use_synth :fm
  use_transpose -12
  play (ring :d, :r, :r, :a, :f5, :r, :a, :r).tick
  sleep 0.25
end
with_fx :reverb do
  live_loop :space_light do
    with_fx :slicer, phase: 0.25 do
      synth :blade, note: :d, release: 8, cutoff: 100, amp: 2
    end
    sleep 8
  end
end

Now, try the other notes in the bass line: :a and :f5. Remember, you don’t need to hit stop, just modify the code whilst the music is playing and hit run again. Also, try different values for the slicer’s phase: opt such as 0.5, 0.75 and 1.

Alles zusammenführen

Zum Schluss lass uns alle bisherigen Ideen in einen neuen Remix von Habanera kombinieren. Du wirst feststellen, dass ich einen weiteren Teil der Basslinie als Kommentar hinzugefügt habe. Wenn Du alles in einen neuen Puffer eingegeben hast, drücke Run, um Dir die Komposition anzuhören. Und jetzt, ohne auf Stopp zu drücken, entkommentiere die zweite Zeile, indem Du das #-Zeichen entfernst, und drücke dann noch einmal Run – wie fantastisch das ist! Jetzt misch alles wie es Dir gefällt – viel Spaß.

use_debug false
bizet_bass = (ring :d, :r, :r, :a, :f5, :r, :a, :r)
#bizet_bass = (ring :d, :r, :r, :Bb, :g5, :r, :Bb, :r)
 
with_fx :reverb, room: 1, mix: 0.3 do
  live_loop :bizet do
    with_fx :slicer, phase: 0.125 do
      synth :blade, note: :d4, release: 8,
        cutoff: 100, amp: 1.5
    end
    16.times do
      tick
      play bizet_bass.look, release: 0.1
      play bizet_bass.look - 12, release: 0.3
      sleep 0.125
    end
  end
end
 
live_loop :ind do
  sample :loop_industrial, beat_stretch: 1,
    cutoff: 100, rate: 1
  sleep 1
end
 
live_loop :drums do
  sample :bd_haus, cutoff: 110
  synth :beep, note: 49, attack: 0,
    release: 0.1
  sleep 0.5
end

- Werde ein Minecraft VJ

Screen 0

Everyone has played Minecraft. You will all have built amazing structures, designed cunning traps and even created elaborate cart lines controlled by redstone switches. How many of you have performed with Minecraft? We bet you didn’t know that you could use Minecraft to create amazing visuals just like a professional VJ.

If your only way of modifying Minecraft was with the mouse, you’d have a tough time changing things fast enough. Luckily for you your Raspberry Pi comes with a version of Minecraft that can be controlled with code. It also comes with an app called Sonic Pi which makes coding Minecraft not only easy but also incredibly fun.

In today’s article we’ll be showing you some of the tips and tricks that we’ve used to create performances in night clubs and music venues around the world.

Lass uns anfangen …

Erste Schritte

Um unsere Grundlagen aufzufrischen, lass uns mit einer einfachen Übung zum Aufwärmen anfangen. Öffne zunächst Deinen Raspberry Pi, dann starte Minecraft und Sonic Pi. Erstelle eine neue Welt in Minecraft, und in Sonic Pi wählst Du einen neuen Puffer, dann schreibe diesen Code:

mc_message "Let's get started..."

Klicke den Run-Button und Du wirst die Meldung im Minecraft-Fenster sehen. Ok, wir können nun loslegen, lass uns Spaß haben …

Sandstürme

When we’re using Minecraft to create visuals we try and think about what will both look interesting and also be easy to generate from code. One nice trick is to create a sand storm by dropping sand blocks from the sky. For that all we need are a few basic fns:

sleep - um eine Verzögerung zwischen Aktionen einzufügen mc_location - um unsere aktuelle Position zu finden mc_set_block- to place sand blocks at a specific location rrand - damit wir Zufallswerte innerhalb eines Bereichs erzeugen können live_loop - to allow us to continually make it rain sand

If you’re unfamiliar with any of the built-in fns such as rrand, just type the word into your buffer, click on it and then hit the keyboard combo Control-i to bring up the built-in documentation. Alternatively you can navigate to the lang tab in the Help system and then look up the fns directly along with all the other exciting things you can do.

Let’s make it rain a little first before unleashing the full power of the storm. Grab your current location and use it to create a few sand blocks up in the sky nearby:

x, y, z = mc_location
mc_set_block :sand, x, y + 20, z + 5
sleep 2
mc_set_block :sand, x, y + 20, z + 6
sleep 2
mc_set_block :sand, x, y + 20, z + 7
sleep 2
mc_set_block :sand, x, y + 20, z + 8

When you hit Run, you might have to look around a little as the blocks may start falling down behind you depending on which direction you’re currently facing. Don’t worry, if you missed them just hit Run again for another batch of sand rain - just make sure you’re looking the right way!

Let’s quickly review what’s going on here. On the first line we grabbed Steve’s location as coordinates with the fn mc_location and placed them into the vars x, y, and z. Then on the next lines we used the mc_set_block fn to place some sand at the same coordinates as Steve but with some modifications. We chose the same x coordinate, a y coordinate 20 blocks higher and then successively larger z coordinates so the sand dropped in a line away from Steve.

Why don’t you take that code and start playing around with it yourself? Try adding more lines, changing the sleep times, try mixing :sand with :gravel and choose different coordinates. Just experiment and have fun!

Live Loops Unleashed

OK, it’s time to get the storm raging by unleashing the full power of the live_loop - Sonic Pi’s magical ability which unleashes the full power of live coding - changing code on-the-fly whilst it’s running!

live_loop :sand_storm do
  x, y, z = mc_location
  xd = rrand(-10, 10)
  zd = rrand(-10, 10)
  co = rrand(70, 130)
  synth :cnoise, attack: 0, release: 0.125, cutoff: co
  mc_set_block :sand, x + xd, y+20, z+zd
  sleep 0.125
end

What fun! We’re looping round pretty quickly (8 times a second) and during each loop we’re finding Steve’s location like before but then generating 3 random values:

xd - Die Abweichung für x. Sie muss zwischen -10 und 10 liegen zd - Die Abweichung für z, auch zwischen -10 und 10 co - ein Cut-Off-Wert für den Tiefpassfilter zwischen 70 und 130

We then use those random values in the fns synth and mc_set_block giving us sand falling in random locations around Steve along with a percussive rain-like sound from the :cnoise synth.

For those of you new to live loops - this is where the fun really starts with Sonic Pi. Whilst the code is running and the sand is pouring down, try changing one of the values, perhaps the sleep time to 0.25 or the :sand block type to :gravel. Now hit run again. Hey Presto! Things changed without the code stopping. This is your gateway to performing like a real VJ. Keep practising and changing things around. How different can you make the visuals without stopping the code?

Epische Blockmuster

Screensman 1

Finally, another great way of generating interesting visuals is to generate huge patterned walls to fly towards and close by. For this effect we’ll need to move from placing the blocks randomly to placing them in an ordered manner. We can do this by nesting two sets of iteration (hit the Help button and navigate to section 5.2 of the tutorial “Iteration and Loops” for more background on iteration). The funny |xd| after the do means that xd will be set for each value of the iteration. So the first time it will be 0, then 1, then 2… etc. By nesting two lots of iteration together like this we can generate all the coordinates for a square. We can then randomly choose block types from a ring of blocks for an interesting effect:

x, y, z = mc_location
bs = (ring :gold, :diamond, :glass)
10.times do |xd|
  10.times do |yd|
    mc_set_block bs.choose, x + xd, y + yd, z
  end
end

Pretty neat. Whilst we’re having fun here, try changing bs.choose to bs.tick to move from a random pattern to a more regular one. Try changing the block types and the more adventurous of you might want to try sticking this within a live_loop so that the patterns keep changing automatically.

Now, for the VJ finale - change the two 10.times to 100.times and hit Run. Kaboom! A Huge gigantic wall of randomly placed bricks. Imagine how long it would take you to build that manually with your mouse! Double-tap space to enter fly-mode and start swooping by for some great visual effects. Don’t stop here though - use your imagination to conjure up some cool ideas and then use the coding power of Sonic Pi to make it real. When you’ve practised enough dim the lights and put on a VJ show for your friends!


- Surfing Random Streams

Back in episode 4 of this tutorial series we took a brief look at randomisation whilst coding up some sizzling synth riffs. Given that randomisation is such an important part of my live coding DJ sets I thought it would be useful to cover the fundamentals in much greater detail. So, get your lucky hat on and let’s surf some random streams!

Es gibt keinen Zufall

The first thing to learn which might really surprise you when playing with Sonic Pi’s randomisation functions is that they’re not actually really random. What does this actually mean? Well, let’s try a couple of tests. First, imagine a number in your head between 0 and 1. Keep it there and don’t tell me. Now let me guess… was it 0.321567? No? Bah, I’m clearly no good at this. Let me have another go, but let’s ask Sonic Pi to choose a number this time. Fire up Sonic Pi v2.7+ and ask it for a random number but again don’t tell me:

print rand

Now for the reveal… was it 0.75006103515625? Yes! Ha, I can see you’re a little sceptical. Perhaps it was just a lucky guess. Let’s try again. Press the Run button again and see what we get… What? 0.75006103515625 again? This clearly can’t be random! You’re right, it’s not.

What’s going on here? The fancy computer science word here is determinism. This just means that nothing is by chance and everything is destined to be. Your version of Sonic Pi is destined to always return 0.75006103515625 in the program above. This might sound pretty useless, but let me assure you that it’s one of the most powerful parts of Sonic Pi. If you stick at it you’ll learn how to rely on the deterministic nature of Sonic Pi’s randomisation as a fundamental building block for your compositions and live coded DJ sets.

Eine zufällige Melodie

When Sonic Pi boots it actually loads into memory a sequence of 441,000 pre-generated random values. When you call a random function such as rand or rrand, this random stream is used to generate your result. Each call to a random function consumes a value from this stream. Therefore the 10th call to a random function will use the 10th value from the stream. Also, every time you press the Run button, the stream is reset for that run. This is why I could predict the result to rand and why the ‘random’ melody was the same every time. Everybody’s version of Sonic Pi uses the exact same random stream which is very important when we start sharing our pieces with each other.

Let’s use this knowledge to generate a repeatable random melody:

8.times do
 play rrand_i(50, 95)
 sleep 0.125
end

Tippe dies in einen freien Puffer und drücke Run. Du wirst eine Melodie hören, die aus zufälligen Noten zwischen 50 und 95 besteht. Wenn sie beendet ist, drücke Run noch einmal, um Dir exakt die gleiche Melodie erneut anzuhören.

Praktische Zufälligkeitsfunktionen

Sonic Pi comes with a number of useful functions for working with the random stream. Here’s a list of some of the most useful:

rand - Gibt einfach den nächsten Wert des Zufallsparameter zurück rrand - Gibt einen Zufallswert innerhalb eines Bereichs zurück rrand_i - Gibt eine zufällige ganze Zahl innerhalb eines Bereichs zurück one_in - Gibt wahr oder falsch mit der angegebenen Wahrscheinlichkeit zurück dice - Imitiert den Wurf eines Würfels und gibt einen Wert zwischen 1 und 6 zurück choose - Wählt einen zufälligen Wert aus einer Liste aus

Check out their documentation in the Help system for detailed information and examples.

Resetting the Stream

Whilst the ability to repeat a sequence of chosen notes is essential to allow you to replay a riff on the dance floor, it might not be exactly the riff you want. Wouldn’t it be great if we could try a number of different riffs and choose the one we liked best? This is where the real magic starts.

We can manually set the stream with the fn use_random_seed. In Computer Science, a random seed is the starting point from which a new stream of random values can sprout out and blossom. Let’s try it:

use_random_seed 0
3.times do
  play rrand_i(50, 95)
  sleep 0.125
end

Great, we get the first three notes of our random melody above: 84, 83 and 71. However, we can now change the seed to something else. How about this:

use_random_seed 1
3.times do
  play rrand_i(50, 95)
  sleep 0.125
end

Interesting, we get 83, 71 and 61 . You might notice that the first two numbers here are the same as the last two numbers before - this isn’t a coincidence.

Remember that the random stream is just a giant list of ‘pre-rolled’ values. Using a random seed simply jumps us to a point in that list. Another way of thinking about it is to imagine a huge deck of pre-shuffled cards. Using a random seed is cutting the deck at a particular point. The fabulous part of this is that it’s precisely this ability to jump around the random stream which gives us huge power when making music.

Let’s revisit our random melody of 8 notes with this new stream resetting power, but let’s also throw in a live loop so we can experiment live whilst it’s playing:

live_loop :random_riff do    
  use_random_seed 0
  8.times do
    play rrand_i(50, 95), release: 0.1
    sleep 0.125
  end
end

Now, whilst it’s still playing, change the seed value from 0 to something else. Try 100, what about 999. Try your own values, experiment and play around - see which seed generates the riff you like best.

Alles zusammenführen

This month’s tutorial has been quite a technical dive into the workings of Sonic Pi’s randomisation functionality. Hopefully it has given you some insight into how it works and how you can start using randomisation in a reliable way to create repeatable patterns within your music. It’s important to stress that you can use repeatable randomisation anywhere you want. For example, you can randomise the amplitude of notes, the timing of the rhythm, the amount of reverb, the current synth, the mix of an FX, etc. etc. In the future we’ll take a close look at some of these applications, but for now let me leave you with a short example.

Type the following into a spare buffer, hit Run, and then start changing the seeds around, hit Run again (whilst it’s still playing) and explore the different sounds, rhythms and melodies you can make. When you find a nice one, remember the seed number so you can get back to it. Finally, when you’ve found a few seeds you like, put on a live coded performance for your friends by simply switching between your favourite seeds to create a full piece.

live_loop :random_riff do
  use_random_seed 10300
  use_synth :prophet
  s = [0.125, 0.25, 0.5].choose
  8.times do
    r = [0.125, 0.25, 1, 2].choose
    n = (scale :e3, :minor).choose
    co = rrand(30, 100)
    play n, release: r, cutoff: co
    sleep s
  end
end
live_loop :drums do
  use_random_seed 2001
  16.times do
    r = rrand(0.5, 10)
    sample :drum_bass_hard, rate: r, amp: rand
    sleep 0.125
  end
end

- Controlling Your Sound

So far during this series we’ve focussed on triggering sounds. We’ve discovered that we can trigger the many synths built into Sonic Pi with play or synth and how to trigger pre-recorded samples with sample. We’ve also looked at how we can wrap these triggered sounds within studio FX such as reverb and distortion using the with_fx command. Combine this with Sonic Pi’s incredibly accurate timing system and you can produce a vast array of sounds, beats and riffs. However, once you’ve carefully selected a particular sound’s options and triggered it, there’s no ability to mess with it whilst it’s playing right? Wrong! Today you’re going to learn something very powerful - how to control running synths.

Ein Grundton

Lass uns einen schönen einfachen Sound erzeugen. Starte Sonic Pi und gib in einen neuen Puffer Folgendes ein:

synth :prophet, note: :e1, release: 8, cutoff: 100

Now press the Run button at the top left to hear a lovely rumbling synth sound. Go ahead, press it again a few times to get a feel for it. OK, done? Let’s start controlling it!

Synth Nodes

A little known feature in Sonic Pi is that the fns play, synth and sample, return something called a SynthNode which represents a running sound. You can capture one of these SynthNodes using a standard variable and then control it at a later point in time. For example, let’s change the value of the cutoff: opt after 1 beat:

sn = synth :prophet, note: :e1, release: 8, cutoff: 100
sleep 1
control sn, cutoff: 130

Let’s look at each line in turn:

Firstly we trigger the :prophet synth using the synth fn as normal. However we also capture the result in a variable called sn. We could have called this variable something completely different such as synth_node or jane - the name doesn’t matter. However, it’s important to choose a name that’s meaningful to you for your performances and for people reading your code. I chose sn as it’s a nice short mnemonic for synth node.

On line 2 we have a standard sleep command. This does nothing special - it just asks the computer to wait for 1 beat before moving onto the next line.

Line 3 is where the control fun starts. Here, we use the control fn to tell our running SynthNode to change the cutoff value to 130. If you hit the Run button, you’ll hear the :prophet synth start playing as before, but after 1 beat it will shift to sound a lot brighter.

Modulierbare Optionen

Most of Sonic Pi’s synths and FX opts may be changed after being triggered. However, this isn’t the case for all of them. For example, the envelope opts attack:, decay:, sustain: and release: can only be set when triggering the synth. Figuring out which opts can and can’t be changed is simple - just head to the documentation for a given synth or FX and then scroll down to the individual option documentation and look for the phrases “May be changed whilst playing” or “Can not be changed once set”. For example, the documentation for the :beep synth’s attack: opt makes it clear that it’s not possible to change it:

Standard: 0 Muss null oder größer sein Kann nicht mehr geändert werden, wenn einmal festgelegt Mit dem aktuellen BPM-Wert skaliert

Mehrere Änderungen

Whilst a synth is running you’re not limited to changing it only once - you’re free to change it as many times as you like. For example, we can turn our :prophet into a mini arpeggiator with the following:

notes = (scale :e3, :minor_pentatonic)
sn = synth :prophet, note: :e1, release: 8, cutoff: 100
sleep 1
16.times do
  control sn, note: notes.tick
  sleep 0.125
end

In this snippet of code we just added a couple of extra things. First we defined a new variable called notes which contains the notes we’d like to cycle through (an arpeggiator is just a fancy name for something that cycles through a list of notes in order). Secondly we replaced our single call to control with an iteration calling it 16 times. In each call to control we .tick through our ring of notes which will automatically repeat once we get to the end (thanks to the fabulous power of Sonic Pi’s rings). For a bit of variety try replacing .tick with .choose and see if you can hear the difference.

Note that we can change multiple opts simultaneously. Try changing the control line to the following and listen for the difference:

control sn, note: notes.tick, cutoff: rrand(70, 130)

Sliding

When we control a SynthNode, it responds exactly on time and instantly changes the value of the opt to the new one as if you’d pressed a button or flicked a switch requesting the change. This can sound rhythmical and percussive - especially if the opt controls an aspect of the timbre such as cutoff:. However, sometimes you don’t want the change to happen instantaneously. Instead, you might want to smoothly move from the current value to the new one as if you’d moved a slider or dial. Of course, Sonic Pi can also do this too using the _slide: opts.

Each opt that can be modified also has a special corresponding _slide: opt that allows you to specify a slide time. For example, amp: has amp_slide: and cutoff: has cutoff_slide:. These slide opts work slightly differently than all the other opts in that they tell the synth note how to behave next time they are controlled. Let’s take a look:

sn = synth :prophet, note: :e1, release: 8, cutoff: 70, cutoff_slide: 2
sleep 1
control sn, cutoff: 130

Notice how this example is exactly the same as before except with the addition of cutoff_slide:. This is saying that next time this synth has its cutoff: opt controlled, it will take 2 beats to slide from the current value to the new one. Therefore, when we use control you can hear the cutoff slide from 70 to 130. It creates an interesting dynamic feel to the sound. Now, try changing the cutoff_slide: time to a shorter value such as 0.5 or a longer value such as 4 to see how it changes the sound. Remember, you can slide any of the modifiable opts in exactly this way and each _slide: value can be totally different so you can have the cutoff sliding slowly, the amp sliding fast and the pan sliding somewhere in between if that’s what you’re looking to create…

Alles zusammenführen

Let’s look at a short example which demonstrates the power of controlling synths after they’ve been triggered. Notice that you can also slide FX just like synths although with a slightly different syntax. Check out section 7.2 of the built-in tutorial for more information on controlling FX.

Kopiere den Code in einen freien Puffer und höre zu. Hör an dieser Stelle nicht auf – spiel mit dem Code herum. Ändere die Wechselzeiten, ändere die Noten, den Synth, die FX und die Pausenzeiten, und probier, etwas ganz Anderes daraus zu machen!

live_loop :moon_rise do
  with_fx :echo, mix: 0, mix_slide: 8 do |fx|
    control fx, mix: 1
    notes = (scale :e3, :minor_pentatonic, num_octaves: 2).shuffle
    sn = synth :prophet , sustain: 8, note: :e1, cutoff: 70, cutoff_slide: 8
    control sn, cutoff: 130
    sleep 2
    32.times do
      control sn, note: notes.tick, pan: rrand(-1, 1)
      sleep 0.125
    end
  end
end

- Tracking the Beat

Last month in this series we took a deep technical dive into the randomisation system underpinning Sonic Pi. We explored how we can use it to deterministically add new levels of dynamic control over our code. This month we’re going to continue our technical dive and turn our attention to Sonic Pi’s unique tick system. By the end of this article you’ll be ticking your way through rhythms and riffs on your way to being a live coding DJ.

Beat Counting

When making music we often want to do a different thing depending on which beat it is. Sonic Pi has a special beat counting system called tick to give you precise control over when a beat actually occurs and even supports multiple beats with their own tempos.

Lass uns spielen – um den Beat zu erweitern, müssen wir nur tickaufrufen. Öffne einen neuen Puffer, gib Folgendes ein und drücke Run:

puts tick #=> 0

This will return the current beat: 0. Notice that even if you press the Run button a few times it will always return 0. This is because each run starts a fresh beat counting from 0. However, whilst the run is still active, we can advance the beat as many times as we want:

puts tick #=> 0
puts tick #=> 1
puts tick #=> 2

Whenever you see the symbol #=> at the end of a line of code it means that that line will log the text on the right-hand-side. For example, puts foo #=> 0 means the code puts foo prints 0 to the log at that point in the program.

Checking the Beat

We’ve seen that tick does two things. It increments (adds one) and returns the current beat. Sometimes we just want to look at the current beat without having to increment it which we can do via look:

puts tick #=> 0
puts tick #=> 1
puts look #=> 1
puts look #=> 1

In this code we tick the beat up twice and then call look twice. We’ll see the following values in the log: 0, 1, 1, 1. The first two ticks returned 0, then 1 as expected, then the two looks just returned the last beat value twice which was 1.

Ringe

So now we can advance the beat with tick and check the beat with look. What next? We need something to tick over. Sonic Pi uses rings for representing riffs, melodies and rhythms and the tick system has been specifically designed to work very closely with them. In fact, rings have their own dot version of tick which does two things. Firstly, it acts like a regular tick and increments the beat. Secondly it looks up the ring value using the beat as the index. Let’s take a look:

puts (ring :a, :b, :c).tick #=> :a

.tick is a special dot version of tick which will return the first value of the ring :a. We can grab each of the values in the ring by calling .tick multiple times:

puts (ring :a, :b, :c).tick #=> :a
puts (ring :a, :b, :c).tick #=> :b
puts (ring :a, :b, :c).tick #=> :c
puts (ring :a, :b, :c).tick #=> :a
puts look                   #=> 3

Take a look at the log and you’ll see :a, :b, :c and then :a again. Notice that look returns 3. Calls to .tick act just like they are regular calls to tick - they increment the local beat.

A Live Loop Arpeggiator

The real power comes when you mix tick with rings and live_loops. When combined we have all the tools we need to both build and understand a simple arpegiator. We need just four things:

A ring containing the notes we want to loop over. A means of incrementing and obtaining the beat. The ability to play a note based on the current beat. A loop structure to keep the arpegiator repeating.

Diese Konzepte findest du alle in dem folgenden Code:

notes = (ring 57, 62, 55, 59, 64)
live_loop :arp do
  use_synth :dpulse
  play notes.tick, release: 0.2
  sleep 0.125
end

Let’s look at each of these lines. First we define our ring of notes which we’ll continually play. We then create a live_loop called :arp which loops round for us. Each time round the live_loop we set our synth to :dpulse and then play the next note in our ring using .tick. Remember that this will increment our beat counter and use the latest beat value as an index into our notes ring. Finally, we wait for an eighth of a beat before looping round again.

Mehrere gleichzeitige Beats

A really important thing to know is that ticks are local to the live_loop. This means that each live_loop has its own independent beat counter. This is much more powerful than having a global metronome and beat. Let’s take a look at this in action:

notes = (ring 57, 62, 55, 59, 64)
with_fx :reverb do
  live_loop :arp do
    use_synth :dpulse
    play notes.tick + 12, release: 0.1
    sleep 0.125
  end
end
live_loop :arp2 do
  use_synth :dsaw
  play notes.tick - 12, release: 0.2
  sleep 0.75
end

Clashing Beats

A big cause of confusion with Sonic Pi’s tick system is when people want to tick over multiple rings in the same live_loop:

use_bpm 300
use_synth :blade
live_loop :foo do
  play (ring :e1, :e2, :e3).tick
  play (scale :e3, :minor_pentatonic).tick
  sleep 1
end

Even though each live_loop has its own independent beat counter, we’re calling .tick twice within the same live_loop. This means that the beat will be incremented twice every time round. This can produce some interesting polyrhythms but is often not what you want. There are two solutions to this problem. One option is to manually call tick at the start of the live_loop and then use .look to look up the current beat in each live_loop. The second solution is to pass a unique name to each call to .tick such as .tick(:foo). Sonic Pi will then create and track a separate beat counter for each named tick you use. That way you can work with as many beats as you need! See the section on named ticks in 9.4 of the built-in tutorial for more information.

Alles zusammenführen

Let’s bring all this knowledge of ticks, rings and live_loops together for a final fun example. As usual, don’t treat this as a finished piece. Start changing things and play around with it and see what you can turn it into. See you next time…

use_bpm 240
notes = (scale :e3, :minor_pentatonic).shuffle
live_loop :foo do
  use_synth :blade
  with_fx :reverb, reps: 8, room: 1 do
    tick
    co = (line 70, 130, steps: 32).tick(:cutoff)
    play (octs :e3, 3).look, cutoff: co, amp: 2
    play notes.look, amp: 4
    sleep 1
  end
end
live_loop :bar do
  tick
  sample :bd_ada if (spread 1, 4).look
  use_synth :tb303
  co = (line 70, 130, steps: 16).look
  r = (line 0.1, 0.5, steps: 64).mirror.look
  play notes.look, release: r, cutoff: co
  sleep 0.5
end

- Sample Slicing

Way back in episode 3 of this Sonic Pi series we looked at how to loop, stretch and filter one of the most famous drum breaks of all time - the Amen Break. In this tutorial we’re going to take this one step further and learn how to slice it up, shuffle the slices and glue it back together in a completely new order. If that sounds a bit crazy to you, don’t worry, it will all become clear and you’ll soon master a powerful new tool for your live coded sets.

Ton als Daten

Before we get started let’s just take a brief moment to understand how to work with samples. By now, you’ve all hopefully played with Sonic Pi’s powerful sampler. If not, there’s no time like the present! Boot up your Raspberry Pi, launch Sonic Pi from the Programming menu, type the following into a fresh buffer and then hit the Run button to hear a pre-recorded drum beat:

sample :loop_amen

A recording of a sound is simply represented as data - lots of numbers between -1 and 1 which represent the peaks and troughs of the sound wave. If we play those numbers back in order, we get the original sound. However, what’s to stop us from playing them back in a different order and creating a new sound?

How are samples actually recorded? It’s actually pretty simple once you understand the basic physics of sound. When you make a sound - for example by hitting a drum, the noise travels through the air in a similar fashion to how the surface of a lake ripples when you throw a pebble into it. When those ripples reach your ears, your eardrum moves sympathetically and converts those movements into the sound you hear. If we wish to record and play back the sound, we therefore need a way of capturing, storing and reproducing those ripples. One way is to use a microphone which acts like an eardrum and moves back and forth as the sound ripples hit it. The microphone then converts its position into a tiny electric signal which is then measured many times a second. These measurements are then represented as a series of numbers between -1 and 1.

If we were to plot a visualisation of the sound it would be a simple graph of data with time on the x axis and microphone/speaker position as a value between -1 and 1 on the y axis. You can see an example of such a graph at the top of the diagram.

Playing Part of a Sample

So, how do we code Sonic Pi to play a sample back in a different order? To answer this question we need to take a look at the start: and finish: opts for sample. These let us control the start and finish positions of our playback of the numbers which represent the sound. The values for both of these opts are represented as a number between 0 and 1 where 0 represents the start of the sample and 1 is the end. So, to play the first half of the Amen Break, we just need to specify a finish: of 0.5:

sample :loop_amen, finish: 0.5

We can add in a start: value to play an even smaller section of the sample:

sample :loop_amen, start: 0.25, finish: 0.5

For fun, you can even have the finish: opt’s value be before start: and it will play the section backwards:

sample :loop_amen, start: 0.5, finish: 0.25

Re-ordering Sample Playback

Now that we know that a sample is simply a list of numbers that can be played back in any order and also how to play a specific part of a sample we can now start having fun playing a sample back in the ‘wrong’ order.

Amen Slices

Let’s take our Amen Break and chop it up into 8 equally-sized slices and then shuffle the pieces around. Take a look at the diagram: at the top A) represents the graph of our original sample data. Chopping it into 8 slices gives us B) - notice that we’ve given each slice a different colour to help distinguish them. You can see each slice’s start and finish values at the top. Finally C) is one possible re-ordering of the slices. We can then play this back to create a new beat. Take a look at the code to do this:

live_loop :beat_slicer do
  slice_idx = rand_i(8)
  slice_size = 0.125
  s = slice_idx * slice_size
  f = s + slice_size
  sample :loop_amen, start: s, finish: f
  sleep sample_duration :loop_amen, start: s, finish: f
end

we choose a random slice to play which should be a random number between 0 and 7 (remember that we start counting at 0). Sonic Pi has a handy function for exactly this: rand_i(8). We then store this random slice index in the variable slice_idx. We define our slice_size which is 1/8 or 0.125. The slice_size is necessary for us to convert our slice_idx into a value between 0 and 1 so we can use it as our start: opt. We calculate the start position s by multiplying the slice_idx by the slice_size. We calculate the finish position f by adding the slice_size to the start position s. We can now play the sample slice by plugging in the s and f values into the start: and finish: opts for sample. Before we play the next slice we need to know how long to sleep which should be the duration of the sample slice. Luckily, Sonic Pi has us covered with sample_duration which accepts all the same opts as sample and simply returns the duration. Therefore, by passing sample_duration our start: and finish: opts, we can find out the duration of a single slice. We wrap all of this code in a live_loop so that we continue to pick new random slices to play.

Alles zusammenführen

Let’s combine everything we’ve seen so far into a final example which demonstrates how we can take a similar approach to combine randomly sliced beats with some bass to create the start of an interesting track. Now it’s your turn - take the code below as a starting point and see if you can take it in your own direction and create something new…

live_loop :sliced_amen do
  n = 8
  s =  line(0, 1, steps: n).choose
  f = s + (1.0 / n)
  sample :loop_amen, beat_stretch: 2, start: s, finish: f
  sleep 2.0  / n
end
live_loop :acid_bass do
  with_fx :reverb, room: 1, reps: 32, amp: 0.6 do
    tick
    n = (octs :e0, 3).look - (knit 0, 3 * 8, -4, 3 * 8).look
    co = rrand(70, 110)
    synth :beep, note: n + 36, release: 0.1, wave: 0, cutoff: co
    synth :tb303, note: n, release: 0.2, wave: 0, cutoff: co
    sleep (ring 0.125, 0.25).look
  end
end

- Code a Probabilistic Sequencer

In a previous episode of this Sonic Pi series we explored the power of randomisation to introduce variety, surprise and change into our live coded tracks and performances. For example, we randomly picked notes from a scale to create never-ending melodies. Today we’re going to learn a new technique which uses randomisation for rhythm - probabilistic beats!

Wahrscheinlichkeit

Before we can start making new beats and synth rhythms we need to take a quick dive into the basics of probability. This might sound daunting and complicated, but really it’s just as simple as rolling a dice - honestly! When you take a regular 6 sided board game dice and roll it what’s actually happening? Well, firstly you’ll roll either a 1, 2, 3, 4, 5 or 6 with exactly the same chance of getting any of the numbers. In fact, given that it’s a 6 sided dice, on average (if you roll lots and lots of times) you’ll throw a 1 every 6 throws. This means you have a 1 in 6 chance of throwing a 1. We can emulate dice rolls in Sonic Pi with the fn dice. Let’s roll one 8 times:

8.times do
  puts dice
  sleep 1
end

Notice how the log prints values between 1 and 6 just as if we’d rolled a real dice ourselves.

Zufällige Beats

Now imagine you had a drum and every time you were about to hit it you rolled a dice. If you rolled a 1, you hit the drum and if you rolled any other number you didn’t. You now have a probabilistic drum machine working with a probability of 1/6! Let’s hear what that sounds like:

live_loop :random_beat do
  sample :drum_snare_hard if dice == 1
  sleep 0.125
end

Let’s quickly go over each line to make sure everything is very clear. First we create a new live_loop called :random_beat which will continually repeat the two lines between do and end. The first of these lines is a call to sample which will play a pre-recorded sound (the :drum_snare_hard sound in this case). However, this line has a special conditional if ending. This means that the line will only be executed if the statement on the right hand side of the if is true. The statement in this case is dice == 1. This calls our dice function which, as we have seen, returns a value between 1 and 6. We then use the equality operator == to check to see if this value is 1. If it is 1, then the statement resolves to true and our snare drum sounds, if it isn’t 1 then the statement resolves to false and the snare is skipped. The second line simply waits for 0.125 seconds before rolling the dice again.

Wahrscheinlichkeiten ändern

Those of you that have played role play games will be familiar with lots of strangely shaped dice with different ranges. For example there is the tetrahedron shaped dice which has 4 sides and even a 20 sided dice in the shape of a icosahedron. The number of sides on the dice changes the chance, or probability of rolling a 1. The fewer sides, the more likely you are to roll a 1 and the more sides the less likely. For example, with a 4 sided dice, there’s a one in 4 chance of rolling a 1 and with a 20 sided dice there’s a one in 20 chance. Luckily, Sonic Pi has the handy one_in fn for describing exactly this. Let’s play:

live_loop :different_probabilities do
  sample :drum_snare_hard if one_in(6)
  sleep 0.125
end

Start the live loop above and you’ll hear the familiar random rhythm. However, don’t stop the code running. Instead, change the 6 to a different value such as 2 or 20 and hit the Run button again. Notice that lower numbers mean the snare drum sounds more frequently and higher numbers mean the snare triggers fewer times. You’re making music with probabilities!

Wahrscheinlichkeiten kombinieren

Things get really exciting when you combine multiple samples being triggered with different probabilities. For example:

live_loop :multi_beat do
  sample :elec_hi_snare if one_in(6)
  sample :drum_cymbal_closed if one_in(2)
  sample :drum_cymbal_pedal if one_in(3)
  sample :bd_haus if one_in(4)
  sleep 0.125
end

Again, run the code above and then start changing the probabilities to modify the rhythm. Also, try changing the samples to create an entirely new feel. For example try changing :drum_cymbal_closed to :bass_hit_c for extra bass!

Wiederholbare Rhythmen

Next, we can use our old friend use_random_seed to reset the random stream after 8 iterations to create a regular beat. Type the following code to hear a much more regular and repeating rhythm. Once you hear the beat, try changing the seed value from 1000 to another number. Notice how different numbers generate different beats.

live_loop :multi_beat do
  use_random_seed 1000
  8.times do
    sample :elec_hi_snare if one_in(6)
    sample :drum_cymbal_closed if one_in(2)
    sample :drum_cymbal_pedal if one_in(3)
    sample :bd_haus if one_in(4)
    sleep 0.125
  end
end

One thing I tend to do with this kind of structure is to remember which seeds sound good and make a note of them. That way I can easily re-create my rhythms in future practice sessions or performances.

Alles zusammenführen

Finally, we can throw in some random bass to give it some nice melodic content. Notice that we can also use our newly discovered probabilistic sequencing method on synths just as well as samples. Don’t leave it at that though - tweak the numbers and make your own track with the power of probabilities!

live_loop :multi_beat do
  use_random_seed 2000
  8.times do
    c = rrand(70, 130)
    n = (scale :e1, :minor_pentatonic).take(3).choose
    synth :tb303, note: n, release: 0.1, cutoff: c if rand < 0.9
    sample :elec_hi_snare if one_in(6)
    sample :drum_cymbal_closed if one_in(2)
    sample :drum_cymbal_pedal if one_in(3)
    sample :bd_haus, amp: 1.5 if one_in(4)
    sleep 0.125
  end
end

- Amplitudenmodulation

This month we’re going to take a deep dive into one of Sonic Pi’s most powerful and flexible audio FX - the :slicer. By the end of this article you will have learned how to manipulate the overall volume of parts of our live coded sound in powerful new ways. This will allow you to create new rhythmic and timbral structures and broaden your sonic possibilities.

Slice that Amp

So, what does the :slicer FX actually do? One way to think about it is that it’s just like having someone play around with the volume control on your TV or home hi-fi. Let’s take a look but first, listen to the deep growl of the following code which triggers the :prophet synth:

synth :prophet, note: :e1, release: 8, cutoff: 70
synth :prophet, note: :e1 + 4, release: 8, cutoff: 80

Now, let’s pipe it through the :slicer FX:


with_fx :slicer do
  synth :prophet, note: :e1, release: 8, cutoff: 70
  synth :prophet, note: :e1 + 4, release: 8, cutoff: 80
end

Hear how the slicer acts like it’s muting and unmuting the audio with a regular beat. Also, notice how the :slicer affects all the audio generated between the do/end blocks. You can control the speed at which it turns the audio on and off with the phase: opt which is short for phase duration. Its default value is 0.25 which means 4 times a second at the default BPM of 60. Let’s make it faster:

with_fx :slicer, phase: 0.125 do
  synth :prophet, note: :e1, release: 8, cutoff: 70
  synth :prophet, note: :e1 + 4, release: 8, cutoff: 80
end

Now, play with different phase: durations yourself. Try longer and shorter values. See what happens when you choose a really short value. Also, try different synths such as :beep or :dsaw and different notes. Take a look at the following diagram to see how different phase: values change the number of amplitude changes per beat.

Dauer

Phase duration is the length of time for one on/off cycle. Therefore smaller values will make the FX switch on and off much faster than larger values. Good values to start playing with are 0.125, 0.25, 0.5 and 1.

Control Waves

By default, the :slicer FX uses a square wave to manipulate the amplitude through time. This is why we hear the amplitude on for a period, then immediately off for a period, then back on again. It turns out that the square wave is just one of 4 different control waves that are supported by :slicer. The others are saw, triangle and (co)sine. Take a look at the diagram below to see what these look like. We can also hear what they sound like. For example, the following code uses (co)sine as the control wave. Hear how the sound doesn’t turn on and off abruptly but instead smoothly fades in and out:

with_fx :slicer, phase: 0.5, wave: 3 do
  synth :dsaw, note: :e3, release: 8, cutoff: 120
  synth :dsaw, note: :e2, release: 8, cutoff: 100
end

Have a play with the different wave forms by changing the wave: opt to 0 for saw, 1 for square, 2 for triangle and 3 for sine. See how different waves sound with different phase: opts too.

Each of these waves can be inverted with the invert_wave: opt which flips it on the y axis. For example, in a single phase the saw wave typically starts high, and slowly goes down before jumping back to the top. With invert_wave: 1 it will start low and slowly go up before jumping back down again. Additionally, the control wave can be started at different points with the phase_offset: opt which should be a value between 0 and 1. By playing around with phase:, wave:, invert_wave: and phase_offset opts you can dramatically change how the amplitude is modified through time.

Dauer

Setting your levels

By default, :slicer switches between amplitude values 1 (fully loud) and 0 (silent). This can be changed with the amp_min: and amp_max: opts. You can use this alongside the sine wave setting to create a simple tremolo effect:

with_fx :slicer, amp_min: 0.25, amp_max: 0.75, wave: 3, phase: 0.25 do
  synth :saw, release: 8
end

This is just like grabbing the volume knob on your hi-fi and moving it up and down just a little so the sound ‘wobbles’ in and out.

Wahrscheinlichkeiten

One of :slicer’s powerful features is its ability to use probability to choose whether or not to turn the slicer on or off. Before the :slicer FX starts a new phase it rolls a dice and based on the result either uses the selected control wave or keeps the amplitude off. Let’s take a listen:

with_fx :slicer, phase: 0.125, probability: 0.6  do
  synth :tb303, note: :e1, cutoff_attack: 8, release: 8
  synth :tb303, note: :e2, cutoff_attack: 4, release: 8
  synth :tb303, note: :e3, cutoff_attack: 2, release: 8
end

Hear how we now have an interesting rhythm of pulses. Try changing the probability: opt to a different value between 0 and 1. Values closer to 0 will have more space between each sound due to the likelihood of the sound being triggered being much lower.

Another thing to notice is that the probability system in the FX is just like the randomisation system accessible via fns such as rand and shuffle. They are both completely deterministic. This means that each time you hit Run you’ll hear exactly the same rhythm of pulses for a given probability. If you would like to change things around you can use the seed: opt to select a different starting seed. This works exactly the same as use_random_seed but only affects that particular FX.

Finally, you can change the ‘resting’ position of the control wave when the probability test fails from 0 to any other position with the prob_pos: opt:

with_fx :slicer, phase: 0.125, probability: 0.6, prob_pos: 1  do
  synth :tb303, note: :e1, cutoff_attack: 8, release: 8
  synth :tb303, note: :e2, cutoff_attack: 4, release: 8
  synth :tb303, note: :e3, cutoff_attack: 2, release: 8
end

Slicing Beats

One really fun thing to do is to use :slicer to chop a drum beat in and out:

with_fx :slicer, phase: 0.125 do
  sample :loop_mika
end

This allows us to take any sample and create new rhythmical possibilites which is a lot of fun. However, one thing to be careful about is to make sure that the tempo of the sample matches the current BPM in Sonic Pi otherwise the slicing will sound totally off. For example, try swapping :loop_mika with the loop_amen sample to hear how bad this can sound when the tempos don’t align.

Tempo ändern

As we have already seen, changing the default BPM with use_bpm will make all the sleep times and synth envelope durations grow or shrink to match the beat. The :slicer FX honours this too, as the phase: opt is actually measured in beats not seconds. We can therefore fix the issue with loop_amen above by changing the BPM to match the sample:

use_sample_bpm :loop_amen
with_fx :slicer, phase: 0.125 do
  sample :loop_amen
end

Alles zusammenführen

Let’s apply all these ideas into a final example that only uses the :slicer FX to create an interesting combination. Go ahead, start changing it and make it into your own piece!

live_loop :dark_mist do
  co = (line 70, 130, steps: 8).tick
  with_fx :slicer, probability: 0.7, prob_pos: 1 do
    synth :prophet, note: :e1, release: 8, cutoff: co
  end
  
  with_fx :slicer, phase: [0.125, 0.25].choose do
    sample :guit_em9, rate: 0.5
  end
  sleep 8
end
live_loop :crashing_waves do
  with_fx :slicer, wave: 0, phase: 0.25 do
    sample :loop_mika, rate: 0.5
  end
  sleep 16
end

- Five Live Coding Techniques

In this month’s Sonic Pi tutorial we’re going to take a look at how you can start treating Sonic Pi like a real instrument. We therefore need to start thinking of code in a completely different way. Live coders think of code in a similar way to how violinists think of their bow. In fact, just like a violinist can apply various bowing techniques to create different sounds (long slow motions vs short fast hits) we will explore five of the basic live coding techniques that Sonic Pi enables. By the end of this article you’ll be able to start practicing for your own live coded performances.

1. Memorise the Shortcuts

The first tip to live coding with Sonic Pi is to start using the shortcuts. For example, instead of wasting valuable time reaching for the mouse, moving it over to the Run button and clicking, you can simply press alt and r at the same time which is much faster and keeps your fingers at the keyboard ready for the next edit. You can find out the shortcuts for the main buttons at the top by hovering the mouse over them. See section 10.2 of the built-in tutorial for the full list of shortcuts.

When performing, one fun thing to do is to add a bit of flair with your arm motion when hitting shortcuts. For example, it’s often good to communicate to the audience when you’re about to make a change - so embellish your movement when hitting alt-r just like a guitarist would do when hitting a big power chord.

2. Manually Layer your Sounds

Now you can trigger code instantly with the keyboard, you can instantly apply this skill for our second technique which is to layer your sounds manually. Instead of ‘composing’ using lots of calls to play, and sample separated by calls to sleep we will have one call to play which we will manually trigger using alt-r. Let’s try it. Type the following code into a fresh buffer:

synth :tb303, note: :e2 - 0, release: 12, cutoff: 90

Now, hit Run and whilst the sound is playing, modify the code in order to drop down four notes by changing it to the following:

synth :tb303, note: :e2 - 4, release: 12, cutoff: 90

Now, hit Run again, to hear both sounds playing at the same time. This is because Sonic Pi’s Run button doesn’t wait for any previous code to finish, but instead starts the code running at the same time. This means you can easily layer lots of sounds manually with minor or major modifications between each trigger. For example, try changing both the note: and the cutoff: opts and then re-trigger.

You can also try this technique with long abstract samples. For example:

sample :ambi_lunar_land, rate: 1

Try starting the sample off, and then progressively halving the rate: opt between hitting Run from 1 to 0.5 to 0.25 to 0.125 and then even try some negative values such as -0.5. Layer the sounds together and see where you can take it. Finally, try adding some FX.

When performing, working with simple lines of code in this way means that an audience new to Sonic Pi has a good chance to follow what you’re doing and relate the code that they can read to the sounds they are hearing.

3. Master Live Loops

When working with more rhythmic music, it can often be hard to manually trigger everything and keep good time. Instead, it is often better to use a live_loop. This provides repetition for your code whilst also giving the ability to edit the code for the next time round the loop. They also will run at the same time as other live_loops which means you can layer them together both with each other and manual code triggers. Take a look at section 9.2 of the built-in tutorial for more information about working with live loops.

When performing, remember to make use of live_loop’s sync: opt to allow you to recover from accidental runtime mistakes which stop the live loop running due to an error. If you already have the sync: opt pointing to another valid live_loop, then you can quickly fix the error and re-run the code to re-start things without missing a beat.

4. Verwende den Hauptmischer

One of Sonic Pi’s best kept secrets is that it has a master mixer through which all sound flows. This mixer has both a low pass filter and a high pass filter built-in, so you can easily perform global modifications to the sound. The master mixer’s functionality can be accessed via the fn set_mixer_control!. For example, whilst some code is running and making sound, enter this into a spare buffer and hit Run:

set_mixer_control! lpf: 50

After you run this code, all existing and new sounds will have a low pass filter applied to them and will therefore sound more muffled. Note that this means that the new mixer values stick until they are changed again. However, if you want, you can always reset the mixer back to its default state with reset_mixer!. Some of the currently supported opts are: pre_amp:, lpf: hpf:, and amp:. For the full list, see the built-in docs for set_mixer_control!.

Use the mixer’s *_slide opts to slide one or many opts values over time. For example, to slowly slide the mixer’s low pass filter down from the current value to 30, use the following:

set_mixer_control! lpf_slide: 16, lpf: 30

You can then slide quickly back to a high value with:

set_mixer_control! lpf_slide: 1, lpf: 130

Bei der Ausführung ist es oft hilfreich, einen Puffer frei zu halten, um, wie jetzt, mit dem Mixer zu arbeiten.

5. Übung

The most important technique for live coding is practice. The most common attribute across professional musicians of all kinds is that they practice playing with their instruments - often for many hours a day. Practice is just as important for a live coder as a guitarist. Practice allows your fingers to memorise certain patterns and common edits so you can type and work with them more fluently. Practice also gives you opportunities to explore new sounds and code constructs.

When performing, you’ll find the more practice you do, the easier it will be for you to relax into the gig. Practice will also give you a wealth of experience to draw from. This can help you understand which kinds of modifications will be interesting and also work well with the current sounds.

Alles zusammenführen

This month, instead of giving you a final example that combines all the things discussed, let’s part by setting down a challenge. See if you can spend a week practicing one of these ideas every day. For example, one day practice manual triggers, the next do some basic live_loop work and the following day play around with the master mixer. Then repeat. Don’t worry if things feel slow and clunky at first - just keep practicing and before you know it you’ll be live coding for a real audience.


- 8 Tips for Live Coding Practice

Last month we took a look at five important techniques for mastering live coding - in other words, we explored how we could use Sonic Pi to approach code in the same way we would approach a musical instrument. One of the important concepts that we discussed was practice. This month we’re going to take a deeper dive into understanding why live coding practice is important and how you might start.

Übe regelmäßig

The most important piece of advice is to make sure you practice regularly. As a rule I typically practice for 1-2 hours a day, but 20 mins is just fine when you’re starting out. Little but often is what you’re aiming for - so if you can only manage 10 minutes, that’s a great start.

Practice tip #1 - start to develop a practice routine. Find a nice time in the day that works for you and try and practice at that time as many days of the week as you can. Before long you’ll be looking forward to your regular session.

Learn to Touch Type

If you watch a professional musician performing on stage you’ll likely notice a few things. Firstly, when they play they don’t stare at their instrument. Their fingers, arms and bodies know which keys to press, strings to pluck or drums to hit without them having to think about it too much. This is known as “muscle memory” and although it might sound like something only professionals can do - it’s just the same as when you first learned to walk or ride a bike - practicing through repetition. Live coders use muscle memory to free their minds from having to think about where to move their fingers so they can focus on the music. This is called touch-typing - typing without having to look at the keyboard.

Practice tip #2 - learn how to touch type. There are many apps, websites and even games which can help you achieve this. Find one you like the look of and stick at it until you can code without looking down.

Code whilst standing

The body of a musician is conditioned for playing their instrument. For example, a trumpet player needs to be able to blow hard, a guitar player needs to be able to grip the fretboard with strength and a drummer needs to be able to continually hit the drums for long periods of time. So, what’s physical about live coding? Just like DJs, live coders typically perform whilst standing up and some even dance whilst they code! If you practice live coding whilst sitting at a desk and then have to get up and stand at a gig, you’ll likely find the difference very difficult and frustrating.

Practice tip #3 - stand whilst you practice. The easiest way to do this is to use a standing height desk. However, if like me you don’t have one at home, there’s a couple of low-fi options. The approach I take is to use an ironing board which happens to work rather well. Another is to stack some boxes or large books on a normal desk and place your keyboard on top of that. Also, make sure you stretch before you start practicing and try and dance a little during the session. Remember, nobody is watching you, so have fun and you’ll feel much more natural on stage.

Practice setting up

Most instruments require some assembly and tuning before they can be played. Unless you’re a rockstar with a bus full of roadies, you’ll have to set up your own instrument before your gig. This is often a stressful time and it is easy for problems to occur. One way to help with this is to incorporate the setup process into your practice sessions.

Practice tip #4 - treat setting up as an important part of your practice. For example, have a box or bag that you can keep your Raspberry Pi and keyboard in etc. Before each practice session, take out all the parts, connect everything, and work through the boot process until you have Sonic Pi running and can make sounds. Once you’ve finished practicing, take the time to carefully pack everything away afterwards. This may take some time at first, but before long you’ll be able to setup and pack everything away incredibly quickly without having to think about it.

Experimentiere musikalisch

Once you’ve set up and are ready to start making music, you might find yourself struggling to know where to start. One problem many people face is that they might have a good idea of the kinds of sounds they want to make, but are frustrated that they can’t produce them. Some people don’t even know what kind of sounds they want to make! The first thing to do is not to worry - this is very common and happens to every musician - even if they’ve been practicing for a long time. It is much more important to be making sounds you don’t like than not making any sounds at all.

Practice tip #5 - spend time making sounds and music you don’t like. Try to make time to explore new sounds and ideas. Don’t worry that it might sound terrible if it’s not the style you’re looking for. When you’re experimenting like this you increase the chance of stumbling over a sound or combination of sounds which you love! Even if 99% of the sounds you make are bad, that 1% might be the riff or intro to your new track. Forget the things you don’t like and remember the parts you do. This is even easier when you’re making music with code - just hit save!

Höre den Code

Viele Musiker können auf ein Notenblatt schauen und die Musik in ihrem Kopf hören, ohne das sie gespielt wird. Das ist eine sehr nützliche Fähigkeit und es lohnt sich auch diese auf deine Live-Coding Übungen zu übertragen: Der wichtige Punkt ist, dass Du eine gewisse Vorstellungen darüber entwickelst, wie sich Dein Code anhören wird. Du musst es natürlich nicht genau in deinem Kopf hören können, aber es ist praktisch, ein Gefühl dafür zu haben, ob sich der Code schnell oder langsam, laut, rhythmisch, melodisch, zufällig usw. anhören wird. Das endgültige Ziel ist dann, diesen Prozess umkehren zu können und zu Musik, die Du dir in deinem Kopf vorstellst, den passenden Code schreiben zu können. Bis dahin wirst du wahrscheinlich sehr viel üben müssen, aber wenn du es dann kannst, wirst du es schaffen auf der Bühne improvisieren zu können und deine Ideen fließend umzusetzen.

Practice tip #6 - write some code into Sonic Pi but don’t hit the Run button. Instead, try to imagine what sound it is going to produce. Then, hit Run, listen, and think about what you got right and what you didn’t. Keep repeating this until it become a natural part of your coding process. When I practice I normally have a good idea of what the code will sound like. However, I still am occasionally surprised, and then I’ll stop and spend some time thinking about why I was wrong. Each time this happens, I learn new tricks which allow me to express myself in new ways.

Remove all distractions

A common problem when practicing is to become distracted with other things. Practicing is hard and requires real discipline regardless of the kind of music you’re making - from jazz to classical to EDM. If you’re struggling to get started or make progress, it’s often too easy to hop on social media, or look something up on the internet etc. If you’ve set yourself a target of 20 minutes of practice, it’s important to try and spend all that time being as productive as possible.

Practice tip #7 - before you start practicing remove as many distractions as possible. For example, disconnect from the internet, put your phone in another room and try to practice in a quiet place where you’re unlikely to be disturbed. Try to focus on coding music and you can return to your distractions when you’ve finished.

Keep a practice diary

When you are practicing, you’ll often find your mind is full of new exciting ideas - new musical directions, new sounds to try out, new functions to write, etc. These ideas are often so interesting that you might stop what you’re doing and start working on the idea. This is another form of distraction!

Practice tip #8 - keep a practice diary by your keyboard. When you get an exciting new idea, temporarily pause your practice session, quickly jot the idea down, then forget about it and carry on practicing. You can then spend some quality time thinking about and working on your ideas after you’ve finished practicing.

Alles zusammenführen

Try to establish a practice routine which incorporates as many of these ideas as possible. Try to keep the sessions as fun as possible but be aware that some practice sessions will be hard and feel a little like work. However, it will all be worth it once you’ve created your first piece or given your first performance. Remember, practice is the key to success!


- Sample Stretching

When people discover Sonic Pi, one of the first things they learn is how simple it is to play pre-recorded sounds using the sample function. For example, you can play an industrial drum loop, hear the sound of a choir or even listen to a vinyl scratch all via a single line of code. However, many people don’t realise that you can actually vary the speed that the sample is played back at for some powerful effects and a whole new level of control over your recorded sounds. So, fire up a copy of Sonic Pi and let’s get started stretching some samples!

Sample verlangsamen

To modify the playback rate of a sample we need to use the rate: opt:

Sample :guit_em9, rate: 1

If we specify a rate: of 1 then the sample is played back at the normal rate. If we want to play it back at half speed we simply use a rate: of 0.5:

sample :guit_em9, rate: 0.5

Dies hat zwei Auswirkungen auf den Ton. Das Sample hört sich tiefer an und es dauert doppelt so lange es abzuspielen (eine Erklärung hierfür findest du in der Seitenleiste). Wir können den Wert von rate immer kleiner machen, indem wir uns 0 annähern. Eine Rate von 0.25 bedeutet ein Viertel der Geschwindigkeit, eine Rate von 0.1 ein Zehntel, usw. Spiele mit niedrige Raten herum und schaue, ob du es schaffst ein tiefes Murren zu erzeugen.

Sample beschleunigen

In addition to making the sound longer and lower using a small rate, we can use higher rates to make the sound shorter and higher. Let’s play with a drum loop this time. First, take a listen to how it sounds at the default rate of 1:

sample :loop_amen, rate: 1

Lassen wir es etwas schneller werden:

sample :loop_amen, rate: 1.5

Ha! We just moved musical genres from old-skool techno to jungle. Notice how the pitch of each drum hit is higher as well as how the whole rhythm speeds up. Now, try even higher rates and see how high and short you can make the drum loop. For example, if you use a rate of 100, the drum loop turns into a click!

Rückwärtsgang

Now, I’m sure many of you are thinking the same thing right now… “what if you use a negative number for the rate?”. Great question! Let’s think about this for a moment. If our rate: opt signifies the speed with which the sample is played back, 1 being normal speed, 2 being double speed, 0.5 being half speed, -1 must mean backwards! Let’s try it on a snare. First, play it back at the normal rate:

sample :elec_filt_snare, rate: 1

Jetzt lass es uns rückwärts abspielen:

sample :elec_filt_snare, rate: -1

Of course, you can play it backwards twice as fast with a rate of -2 or backwards at half speed with a rate of -0.5. Now, play around with different negative rates and have fun. It’s particularly amusing with the :misc_burp sample!

Sample, Rate and Pitch

One of the effects of rate modification on samples is that faster rates result in the sample sounding higher in pitch and slower rates result in the sample sounding lower in pitch. Another place you may have heard this effect in every day life is when you’re cycling or driving past a beeping pedestrian crossing - as you’re heading towards the sound source the pitch is higher than when you’re moving away from the sound - the so-called Doppler effect. Why is this?

Let’s consider a simple beep which is represented by a sine wave. If we use an oscilloscope to plot a beep, we’ll see something like Figure A. If we plot a beep an octave higher, we’ll see Figure B and an octave lower will look like Figure C. Notice that the waves of higher notes are more compact and the waves of lower notes are more spread out.

A sample of a beep is nothing more than a lot of numbers (x, y, coordinates) which when plotted onto a graph will re-draw the original curves. See figure D where each circle represents a coordinate. To turn the coordinates back into audio, the computer works through each x value and sends the corresponding y value to the speakers. The trick here is that the rate at which the computer works through the x numbers does not have to be the same as the rate with which they were recorded. In other words, the space (representing an amount of time) between each circle can be stretched or compressed. So, if the computer walks through the x values faster than the original rate, it will have the effect of squashing the circles closer together which will result in a higher sounding beep. It will also make the beep shorter as we will work through all the circles faster. This is shown in Figure E.

Finally, one last thing to know is that a mathematician called Fourier proved that any sound is actually lots and lots of sine waves all combined together. Therefore, when we compress and stretch any recorded sound we’re actually stretching and compressing many sine waves all at the same time in exactly this manner.

Pitch Bending

As we’ve seen, using a faster rate will make the sound higher in pitch and a slower rate will make the sound lower in pitch. A very simple and useful trick is to know that doubling the rate actually results in the pitch being an octave higher and inversely halving the rate results in the pitch being an octave lower. This means that for melodic samples, playing it alongside itself at double/half rates actually sounds rather nice:

sample :bass_trance_c, rate: 1
sample :bass_trance_c, rate: 2
sample :bass_trance_c, rate: 0.5

However, what if we just want to alter the rate such that the pitch goes up one semitone (one note up on a piano)? Sonic Pi makes this very easy via the rpitch: opt:

sample :bass_trance_c
sample :bass_trance_c, rpitch: 3
sample :bass_trance_c, rpitch: 7

If you take a look at the log on the right, you’ll notice that an rpitch: of 3 actually corresponds to a rate of 1.1892 and a rpitch: of 7 corresponds to a rate of 1.4983. Finally, we can even combine rate: and rpitch: opts:

sample :ambi_choir, rate: 0.25, rpitch: 3
sleep 3
sample :ambi_choir, rate: 0.25, rpitch: 5
sleep 2
sample :ambi_choir, rate: 0.25, rpitch: 6
sleep 1
sample :ambi_choir, rate: 0.25, rpitch: 1

Alles zusammenführen

Let’s take a look at a simple piece which combines these ideas. Copy it into an empty Sonic Pi buffer, hit play, listen to it for a while and then use it as a starting point for your own piece. See how much fun it is to manipulate the playback rate of samples. As an added exercise try recording your own sounds and play around with the rate to see what crazy sounds you can make.

live_loop :beats do
  sample :guit_em9, rate: [0.25, 0.5, -1].choose, amp: 2
  sample :loop_garzul, rate: [0.5, 1].choose
  sleep 8
end
 
live_loop :melody do
  oct = [-1, 1, 2].choose * 12
  with_fx :reverb, amp: 2 do
    16.times do
      n = (scale 0, :minor_pentatonic).choose
      sample :bass_voxy_hit_c, rpitch: n + 4 + oct
      sleep 0.125
    end
  end
end

- Additive Synthesis

This is the first of a short series of articles on how to use Sonic Pi for sound design. We’ll be taking a quick tour of a number of different techniques available for you to craft your own unique sound. The first technique we’ll look at is called additive synthesis. This may sound complicated - but if we expand each word slightly the meaning pops right out. Firstly, additive means a combination of things and secondly synthesis means to create sound. Additive synthesis therefore means nothing more complicated than combining existing sounds to create new ones. This synthesis technique dates back a very long time - for example, pipe organs in the middle ages had lots of slightly different sounding pipes which you could enable or disable with stops. Pulling out the stop for a given pipe ‘added it to the mix’ making the sound richer and more complex. Now, let’s see how we can pull out all the stops with Sonic Pi.

Simple Combinations

Let’s start with the most basic sound there is - the humble pure-toned sine wave:

synth :sine, note: :d3

Now, let’s see how this sounds combined with a square wave:

synth :sine, note: :d3
synth :square, note: :d3

Notice how the two sounds combine to form a new, richer sound. Of course, we don’t have to stop there, we can add as many sounds as we need. However, we need to be careful with how many sounds we add together. Just like when we mix paints to create new colours, adding too many colours will result in a messy brown, similarly - adding too many sounds together will result in a muddy sound.

Blending

Let’s add something to make it sound a little brighter. We could use a triangle wave at an octave higher (for that high bright sound) yet only play it at amp 0.4 so it adds something extra to the sound rather than taking it over:

synth :sine, note: :d3
synth :square, note: :d3
synth :tri, note: :d4, amp: 0.4

Now, try creating your own sounds by combining 2 or more synths at different octaves and amplitudes. Also, note that you can play around with each synth’s opts to modify each source sound before it is mixed in for even more combinations of sounds.

Verstimmung

So far, when combining our different synths we’ve used either the same pitch or switched octave. How might it sound if we didn’t stick to octaves but instead chose a slightly higher or lower note? Let’s try it:

detune = 0.7
synth :square, note: :e3
synth :square, note: :e3 + detune

If we detune our square waves by 0.7 notes we hear something that perhaps doesn’t sound in tune or correct - a ‘bad’ note. However, as we move closer to 0 it will sound less and less out of tune as the pitches of the two waves get closer and more similar. Try it for yourself! Change the detune: opt value from 0.7 to 0.5 and listen to the new sound. Try 0.2, 0.1, 0.05, 0. Each time you change the value, take a listen and see if you can hear how the sound is changing. Notice that low detune values such as 0.1 produce a really nice ‘thick’ sound, with both slightly different pitches interacting with each other in interesting, often surprising, ways.

Einige der integrierten Synths haben bereits eine Option zur Verstimmung, die das ganze für einem Synth machen. Probiere die Option detune: von :dsaw, :dpulse und :dtri aus.

Amplitudenmodellierung

Another way we can finely craft our sound is to use a different envelope and options for each synth trigger. For example this will allow you to make some aspects of the sound percussive and other aspects ring out for a period of time.

detune = 0.1
synth :square, note: :e1, release: 2
synth :square, note: :e1 + detune, amp: 2, release: 2
synth :gnoise, release: 2, amp: 1, cutoff: 60
synth :gnoise, release: 0.5, amp: 1, cutoff: 100
synth :noise, release: 0.2, amp: 1, cutoff: 90

In the example above I have mixed in a noisy percussive element to the sound along with some more persistent background rumbling. This was achieved firstly by using two noise synths with middling cutoff values (90 and 100) using short release times along with a noise with a longer release time but with a low cutoff value (which makes the noise less crisp and more rumbly.)

Alles zusammenführen

Let’s combine all these techniques to see if we can use additive synthesis to re-create a basic bell sound. I’ve broken this example into four sections. Firstly we have the ‘hit’ section which is the initial onset part of the bell sound - so uses a short envelope (e.g. a release: of around 0.1). Next we have the long ringing section in which I’m using the pure sound of the sine wave. Notice that I’m often increasing the note by roughly 12 and 24 which are the number of notes in one and two octaves. I have also thrown in a couple of low sine waves to give the sound some bass and depth. Finally, I used define to wrap my code in a function which I can then use to play a melody. Try playing your own melody and also messing around with the contents of the :bell function until you create your own crazy sound to play with!

define :bell do |n|
  # Triangle waves for the 'hit'
  synth :tri, note: n - 12, release: 0.1
  synth :tri, note: n + 0.1, release: 0.1
  synth :tri, note: n - 0.1, release: 0.1
  synth :tri, note: n, release: 0.2
  # Sine waves for the 'ringing'
  synth :sine, note: n + 24, release: 2
  synth :sine, note: n + 24.1, release: 2
  synth :sine, note: n + 24.2, release: 0.5
  synth :sine, note: n + 11.8, release: 2
  synth :sine, note: n, release: 2
  # Low sine waves for the bass
  synth :sine, note: n - 11.8, release: 2
  synth :sine, note: n - 12, release: 2
end
# Play a melody with our new bell!
bell :e3
sleep 1
bell :c2
sleep 1
bell :d3
sleep 1
bell :g2

- Subtractive Synthesis

This is the second in a series of articles on how to use Sonic Pi for sound design. Last month we looked at additive synthesis which we discovered was the simple act of playing multiple sounds at the same time to make a new combined sound. For example we could combine different sounding synths or even the same synth at different pitches to build a new complex sound from simple ingredients. This month we’ll look at a new technique commonly called subtractive synthesis which is simply the act of taking an existing complex sound and removing parts of it to create something new. This is a technique which is commonly associated with the sound of analog synthesisers of the 1960s and 1970s but also with the recent renaissance of modular analog synths through popular standards such as Eurorack.

Despite this sounding like a particularly complicated and advanced technique, Sonic Pi makes it surprisingly simple and easy - so let’s dive right in.

Complex Source Signal

For a sound to work well with subtractive synthesis, it typically needs to be fairly rich and interesting. This doesn’t mean we need something hugely complex - in fact, just a standard :square or :saw wave will do:

synth :saw, note: :e2, release: 4

Notice that this sound is already pretty interesting and contains many different frequencies above :e2 (the second E on a piano) which add to create the timbre. If that didn’t make much sense to you, try comparing it with the :beep:

synth :beep, note: :e2, release: 4

As the :beep synth is just a sine wave, you’ll hear a much purer tone and only at :e2 and none of the high crispy/buzzy sounds which you heard in the :saw. It’s this buzziness and variation from a pure sine wave that we can play with when we use subtractive synthesis.

Filter

Once we have our raw source signal, the next step is to pass it through a filter of some kind which will modify the sound by removing or reducing parts of it. One of the most common filters used for subtractive synthesis is something called a low pass filter. This will allow all the low parts of the sound through but will reduce or remove the higher parts. Sonic Pi has a powerful yet simple to use FX system that includes a low pass filter, called :lpf. Let’s play with it:

with_fx :lpf, cutoff: 100 do
  synth :saw, note: :e2, release: 4
end

If you listen carefully you’ll hear how some of that buzziness and crispiness has been removed. In fact, all the frequencies in the sound above note 100 have been reduced or removed and only the ones below are still present in the sound. Try changing that cutoff: point to lower notes, say 70 and then 50 and compare the sounds.

Of course, the :lpf isn’t the only filter you can use to manipulate the source signal. Another important FX is the high pass filter referred to as :hpf in Sonic Pi. This does the opposite to :lpf in that it lets the high parts of the sound through and cuts off the low parts.

with_fx :hpf, cutoff: 90 do
  synth :saw, note: :e2, release: 4
end

Notice how this sounds much more buzzy and raspy now that all the low frequency sounds have been removed. Play around with the cutoff value - notice how lower values let more of the original bass parts of the source signal through and higher values sound increasingly tinny and quiet.

Low Pass Filter

The low pass filter is such an important part of every subtractive synthesis toolkit that it’s worth taking a deeper look at how it works. This diagram shows the same sound wave (the :prophet synth) with varying amounts of filtering. At the top, section A shows the audio wave with no filtering. Notice how the wave form is very pointy and contains lots of sharp edges. It is these hard, sharp angles that produce the high crispy/buzzy parts of the sound. Section B shows the low pass filter in action - notice how it is less pointy and more rounded than the wave form above. This means that the sound will have fewer high frequencies giving it a more mellow rounded feel. Section C shows the low pass filter with a fairly low cutoff value - this means that even more of the high frequencies have been removed from the signal resulting in an even softer, rounder wave form. Finally, notice how the size of the wave form, which represents the amplitude, decreases as we move from A to C. Subtractive synthesis works by removing parts of the signal which means that the overall amplitude is reduced as the amount of filtering that is taking place increases.

Filter Modulation

So far we’ve just produced fairly static sounds. In other words, the sound doesn’t change in any way for the entirety of its duration. Often you might want some movement in the sound to give the timbre some life. One way to achieve this is via filter modulation - changing the filter’s options through time. Luckily Sonic Pi gives you powerful tools to manipulate an FX’s opts through time. For example, you can set a slide time to each modulatable opt to specify how long it should take for the current value to linearly slide to the target value:

with_fx :lpf, cutoff: 50 do |fx|
  control fx, cutoff_slide: 3, cutoff: 130
  synth :prophet, note: :e2, sustain: 3.5
end

Let’s take a quick look at what’s going on here. Firstly we start an :lpf FX block as normal with an initial cutoff: of a very low 20. However, the first line also finishes with the strange |fx| at the end. This is an optional part of the with_fx syntax which allows you to directly name and control the running FX synth. Line 2 does exactly this and controls the FX to set the cutoff_slide: opt to 4 and the new target cutoff: to be 130. The FX will now start sliding the cutoff: opt’s value from 50 to 130 over a period of 3 beats. Finally we also trigger a source signal synth so we can hear the effect of the modulated low pass filter.

Alles zusammenführen

This is just a very basic taster of what’s possible when you use filters to modify and change a source sound. Try playing with Sonic Pi’s many built-in FX to see what crazy sounds you can design. If your sound feels too static, remember you can start modulating the options to create some movement.

Let’s finish by designing a function which will play a new sound created with subtractive synthesis. See if you can figure out what’s going on here - and for the advanced Sonic Pi readers out there - see if you can work out why I wrapped everything inside a call to at (please send answers to @samaaron on Twitter).

define :subt_synth do |note, sus|
  at do
    with_fx :lpf, cutoff: 40, amp: 2 do |fx|
      control fx, cutoff_slide: 6, cutoff: 100
      synth :prophet, note: note, sustain: sus
    end
    with_fx :hpf, cutoff_slide: 0.01 do |fx|
      synth :dsaw, note: note + 12, sustain: sus
      (sus * 8).times do
        control fx, cutoff: rrand(70, 110)
        sleep 0.125
      end
    end
  end
end
subt_synth :e1, 8
sleep 8
subt_synth :e1 - 4, 8

- Creative coding in the classroom with Sonic Pi

(This article was published in issue 9 of the Hello World Magazine)

Code is one of the most creative media that humans have created. The initially obscure symbols of parentheses and lambdas are not just deeply rooted in science and mathematics, they are the closest we have managed to get to casting the same kind of magical spells as Gandalf and Harry Potter. I believe that this provides a powerful means of engagement in our learning spaces. Through the magic of code we are able to conjure up individually meaningful stories and learning experiences.

We are surrounded by magical experiences. From the sleight of hand of a stage magician making the ball disappear into thin air, to the wonder of seeing your favourite band perform on a big stage. It is these “wow” moments that inspire us to pick up a magic book and learn the French Drop or to start jamming power chords on an old guitar. How might we create similarly deep and lasting senses of wonder that will motivate people to practice and learn the fundamentals of programming?

Musical Engines and Notation

The histories of music and computers have been intricately woven together since the inception of computing machines, or “engines” as Charles Babbage’s powerful analytical engine was called. Back in 1842 the Mathematician Ada Lovelace, who worked very closely with Babbage, saw the creative potential of these engines. Whilst these first engines had originally been designed to accurately solve hard maths problems, Ada dreamt about making music with them:

”..the engine might compose elaborate and scientific pieces of music of any degree of complexity or extent.” Ada Lovelace, 1842.

Of course, today in 2019 much of our music, regardless of genre, has either been composed, produced or mastered with a digital computer. Ada’s dream came true. It is even possible to trace the history back even further. If you see coding as the art of writing sequences of special symbols that instruct a computer to do specific things, then musical composition is a very similar practice. In Western music, the symbols are black dots positioned on a stave of lines that tell the musician which notes to play and when. Intriguingly, if we trace the roots of Western music notation back to the Italian Benedictine monk, Guido d’Arezzo, we find that the dots and lines system that modern orchestras use is just one of a number of notation systems he worked on. Some of the others were much closer to what we might now see as code.

Get (Abfragen des Werts)

Sonic Pi Performances

Live-Coding

Sonic Pi has been used to perform in a wide range of venues such as school halls, nightclubs, outdoor stages at musical festivals, college chapels and prestigious music venues. For example the amazing Convo project which brought 1000 children together in the Royal Albert Hall to perform an ambitious new composition by composer Charlotte Harding. The piece was written for traditional instruments, choirs, percussion and Sonic Pi code. The pop-artist Jylda also performed with Sonic Pi in the Sage Gateshead for the Thinking Digital Conference, where she created a unique live-coded improvised remix of her song Reeled.

Sonic Pi in the Royal Albert Hall Sonic Pi used as one of the instruments as part of Convo at the Royal Albert Hall. Photo credit: Pete Jones.

Live coding in the classroom

Sonic Pi is a code-based music creation and performance tool that builds on all of these ideas. Unlike the majority of computing education software, it is both simple enough to use for education and also powerful enough for professionals. It has been used to perform in international music festivals, used to compose in a range of styles from classical, EDM and heavy metal, and was even reviewed in the Rolling Stone magazine. It has a diverse community of over 1.5 million live coders with a variety of backgrounds all learning and sharing their ideas and thoughts through the medium of code. It is free to download for Mac, PC and Raspberry Pi and includes a friendly tutorial that assumes you know nothing about either code or music.

Sonic Pi was initially conceived as a response to the UK’s newly released Computing curriculum in 2014. The goal was to find a motivating and fun way to teach the fundamentals of programming. It turns out that there is a lot in common and it’s huge fun to explain sequencing as melody, iteration as rhythm, conditionals as musical variety. I developed the initial designs and first iterations of the platform with Carrie Anne Philbin, who brought a teacher’s perspective to the project. Since then, Sonic Pi has undergone iterative improvements thanks to the feedback gained from observing learners and collaborating directly with educators in the classroom. A core design philosophy was to never add a feature that couldn’t be easily taught to a 10 year old child. This meant that most ideas had to be heavily refined and reworked until they were simple enough. Making things simple whilst keeping them powerful continues to be the hardest part of the project.

In order to provide the magical motivation, Sonic Pi’s design was never limited to a pure focus on education. Ideally there would be famous musicians and performers using Sonic Pi as a standard instrument alongside guitars, drums, vocals, synths, violins, etc. These performers would then act as motivational role models demonstrating the creative potential of code. For this to be possible sufficient focus and effort therefore had to be placed on making it a powerful instrument whilst still keeping it simple enough for 10 year olds to pick up. In addition to educators, I also worked directly with a variety of different artists in classrooms, art galleries, studios and venues in the early stages of Sonic Pi’s development. This provided essential feedback which enabled Sonic Pi to grow and ultimately flourish as a tool for creative expression.

There were a number of exciting and unexpected side effects of this dual focus on education and professional musicians. Many of the features are beneficial to both groups. For example, a lot of effort has been put into making error messages more friendly and useful (rather than being a huge complicated mess of jargon). This turns out to be very useful when you write a bug while performing in front of thousands of people. Additionally, functionality such as playing studio quality audio samples, adding audio effects, providing access to live audio from the microphone all turn out to make the learning experience more fun, rewarding and ultimately meaningful.

The Sonic Pi community continues to grow and share amazing code compositions, lesson plans, musical algorithms, and much more. Much of this happens on our friendly forum in_thread (in-thread.sonic-pi.net) which is home to a very diverse group of people that includes educators, musicians, programmers, artists and makers. It is a real joy to see people learn to use code to express themselves in new ways and for that in turn to inspire others to do the same.

- Some fun capabilities

From a Computer Science perspective, Sonic Pi provides you with the building blocks to teach you the basics as found in the UK’s curriculum such as sequencing, iteration, conditionals, functions, data structures, algorithms, etc. However, it also builds on a number of important and relevant concepts which have become adopted in mainstream industry such as concurrency, events, pattern matching, distributed computing and determinism - all whilst keeping things simple enough to explain to a 10 year old child.

Get (Abfragen des Werts)

play 70

A melody can be constructed with one more command, sleep:

play 72
sleep 0.5
play 75
sleep 0.5
play 79

In this example, we play the note 70 (roughly the 70th note on a piano), wait for 1 second, play note 72, wait for half a second and then play note 75. What’s interesting here is that with just two commands we have access to pretty much all of Western notation (which notes to play and when) and learners can code any melody they’ve ever heard. This leads to huge variety in expressive outcomes whilst focussing on the same computing concept: sequencing in this case.

Taking ideas from the professional music world, we can also play back any recorded sound. Sonic Pi can play any audio file on your computer but also has a number of sounds built-in to make things easy to get started:

sample :loop_amen

This code will play back the drum break which was a pillarstone to early hip-hop, Drum and Bass and Jungle. For example, a number of early hip-hop artists played this drum break back at half speed to give it a more laid-back feeling:

sample :loop_amen, rate: 0.5

In the 90s a number of music scenes burst out of new technology which enabled artists to take drum breaks like this apart and reassemble in a different order. For example:

live_loop :jungle do 
 sample :loop_amen, onset: pick
 sleep 0.125
end

In diesem Beispiel führen wir eine einfache Schleife mit dem Namen :jungle ein, die einen zufälligen Trommelschlag aus unserem Audio Sample auswählt, eine achtel Sekunde wartet und dann einen weiteren Trommelschlag abspielt. Das Ergebnis ist eine endlose Folge von Trommelschlägen zum dabei Tanzen, während Du erfährst, was eine Schleife ist.


- Unentbehrliche Kenntnisse

In diesem Kapitel findest Du sehr nützliches - nein, unentbehrliches - Wissen, um das Bestmögliche aus Sonic Pi herauszuholen.

Wir behandeln viele der verfügbaren Tastaturkürzel, zeigen, wie Du Deine Ergebnisse mit anderen teilen kannst, und geben Dir Tipps für Auftritte mit Sonic Pi.


10.1 - Tastaturkürzel

Sonic Pi ist zu gleichen Teilen Musikinstrument und Programmierumgebung. Mit Tastaturkürzeln kannst Du Sonic Pi viel effizienter und natürlicher spielen - insbesondere, wenn Du live vor Publikum spielst.

Sehr vieles in Sonic Pi kann mit der Tastatur gesteuert werden. Je vertrauter Du mit Sonic Pi wirst, umso mehr wirst Du diese Tastaturkürzel verwenden. Ich selbst tippe, ohne die Tastatur anzusehen (und kann Dir nur empfehlen, das Zehnfingersystem auch zu lernen). Und ich bin jedes Mal frustriert, wenn ich zur Maus greifen muss, denn das macht mich langsam. Deshalb benutze ich ständig Tastaturkürzel!

Wer die Tastaturkürzel beherrscht, kann auch seine Tastatur viel effektiver benutzen und in kürzester Zeit wirst Du programmieren wie ein Profi.

Versuche aber nicht, alle auf einmal zu lernen, merke Dir erst einmal die, die Du am häufigsten brauchst. Später kannst Du Dein Repertoire nach und nach erweitern.

Konsistenz auf verschiedenen Plattformen

Stell Dir vor, Du lernst Klarinette. Du kannst davon ausgehen, dass alle Klarinetten aller Marken gleiche Mechaniken und Fingersätze haben. Hätten sie das nicht, könntest Du nicht ohne weiteres zwischen verschiedenen Klarinetten hin- und her wechseln. Sondern müsstest immer bei einer Marke bleiben.

Unglücklicherweise haben die drei wesentlichen Betriebssysteme (Linux, Mac OS X und Windows) alle ihre eigenen typischen Tastaturkürzel für Aktionen wie z.B. Kopieren, Ausschneiden und Einfügen. Sonic Pi nutzt diese Standards wo immer möglich. Jedoch liegt die Priorität auf plattformübergreifender Konsistenz innerhalb von Sonic Pi, nicht auf dem Versuch, die Standards der jeweiligen Plattform vollumfänglich zu erfüllen. Das bedeutet, dass die in Sonic Pi auf dem Raspberry Pi gelernten Tastaturkürzel ebenso auf einem Mac oder PC mit Sonic Pi funktionieren.

Control und Meta

Für diese Konsistenz müssen wir auch die Namen der Tastaturkürzel entsprechend auswählen. In Sonic Pi verwenden wir die Namen Control und Meta für die beiden wichtigsten Kombinationstasten. Control (Ctrl - oder Strg für “Steuerung” auf deutschen Tastaturen) ist auf allen Plattformen gleich. Auf Linux und Windows ist Meta die Alt-Taste, während Meta auf dem Mac die Command-Taste ist. Um konsistent zu bleiben, nutzen wir den Begriff Meta - diesem musst Du mental die passende Taste Deines Betriebssystems zuordnen.

Abkürzungen

Um die Dinge einfach und lesbar zu halten, werden wir die Abkürzungen C- für Control und eine weitere Taste sowie M- für Meta und eine weitere Taste verwenden. Wenn ein Tastaturkürzel beispielsweise erfordert, dass Du Meta und r gleichzeitig drückst, werden wir die Abkürzung M-r verwenden. Der Bindestrich in der Mitte (-) bedeutet nur “zur gleichen Zeit.”

Hier sind ein paar der Tastaturkürzel, die ich am nützlichsten finde.

Starten und Stoppen

Du musst nicht immer zur Maus greifen, um Deinen Code auszuführen. Drücke stattdessen einfach M-r zum Abspielen. Mit M-s stoppst Du die Musik.

Ohne die Tastenkürzel zur Navigation bin ich verloren. Deshalb empfehle ich Dir dringend, diese Kürzel zu lernen. Sie funktionieren besonders gut, wenn Du das Zehnfingersystem schon halbwegs beherrschst, da sie normale Buchstaben verwenden - so musst Du Deine Hand nicht zur Maus oder zu den Pfeiltasten bewegen.

Mit C-a springst Du an den Anfang, mit C-e ans Ende einer Zeile. Eine Zeile nach oben geht es mit C-p, eine nach unten mit C-n, ein Zeichen vorwärts mit C-f und eines nach hinten mit C-b. Du kannst auch alle Zeichen von der aktuellen Cursorposition bis zum Ende der Zeile mit C-k löschen.

Code aufräumen

Um Deinen Code sauber einzurücken, drücke M-m.

Hilfesystem

Zum Hilfesystem kommst Du mit M-i. Noch hilfreicher ist allerdings C-i, damit wird Dir für das Wort, auf dem der Cursor gerade steht, sofort die passende Stelle in der Dokumentation gezeigt. Sofortige Hilfe!

Eine Liste aller Tastaturkürzel ist in Kapitel 10.2 “Cheatsheet für Tastenkürzel” enthalten.


10.2 - Cheatsheet für Tastaturkürzel

In diesem Abschnitt findest Du eine Zusammenfassung der wichtigsten Tastenkürzel von Sonic Pi. In Abschnitt 10.1 erklärte ich Dir bereits, warum diese so nützlich sind.

Konventionen

Wir verwenden die folgenden Konventionen (Meta ist Alt auf Windows und Linux und Cmd auf dem Mac wie in 10.1 beschrieben):

C-a bedeutet, die Control (Strg auf deutschen Tastaturen) zu drücken und zu halten, dabei a zu drücken und danach beide Tasten wieder los zu lassen. M-r bedeutet, Meta zu drücken und zu halten, dabei die r-Taste zu drücken und danach beide wieder los zu lassen. S-M-z bedeutet, die Meta-Taste zu drücken und zu halten, dann Shift zu drücken und zu halten und schließlich die z-Taste und alle drei Tasten gleichzeitig wieder los zu lassen. C-M-f bedeutet, Control zu drücken und zu halten, dann Meta zu drücken und zu halten, dann f zu drücken und alle drei Tasten gleichzeitig wieder los zu lassen.

Steuerung der Hauptanwendung

M-r - Code ausführen M-s - Code-Ausführung stoppen M-i - Hilfesystem ein-/ausschalten M-p - Einstellungen ein-/ausschalten M-< - Zum Puffer links wechseln M-> - Zum Puffer rechts wechseln M-+ - Schrift des Puffers vergrößern M-- - Schrift des Puffers verkleinern

Auswahl/Kopieren/Einfügen

M-a - Alles auswählen M-c - Auswahl in den Zwischenspeicher kopieren M-] - Auswahl in den Zwischenspeicher kopieren M-x - Auswahl ausschneiden und im Zwischenspeicher ablegen C-] - Auswahl ausschneiden und im Zwischenspeicher ablegen C-k - Text ab Cursor bis zum Ende der Zeile ausschneiden M-v - Inhalt des Zwischenspeichers in den Editor kopieren C-y - Inhalt des Zwischenspeichers in den Editor kopieren C-SPACE - Markierung setzen. Textauswahl ab jetzt von hier bis zum Cursor. C-g löscht die Markierung.

Text-Manipulation

M-m - Gesamten Code ausrichten Tab - Aktuelle Codezeile oder Auswahl ausrichten C-l - Editor zentrieren M-/ - Aktuelle Codezeile auskommentieren C-t - Zeichen vertauschen M-u - Das nächste Wort oder Auswahl in Großbuchstaben wandeln. M-l - Das nächste Wort oder Auswahl in Kleinbuchstaben wandeln.

C-a - An den Anfang der Zeile springen C-e - Ans Ende der Zeile springen C-p - Zur vorherigen Zeile springen C-n - Zur nächsten Zeile springen C-f - Ein Zeichen vorwärts C-b - Ein Zeichen zurück M-f - Ein Wort nach vorne M-b - Ein Wort zurück C-M-n - Zeile oder Auswahl nach unten schieben C-M-p - Zeile oder Auswahl nach unten schieben S-M-u - 10 Zeilen nach oben springen S-M-d - 10 Zeilen nach unten springen M-< - Zum Anfang des Puffers gehen M-> - Zum Ende des Puffers gehen

Löschen

C-h - Vorangegangenes Zeichen löschen C-d - Nächstes Zeichen löschen

Fortgeschrittene Funktionen des Editors

C-i - Dokumentation für das Wort unter dem Cursor anzeigen M-z - Rückgängig (“Undo”) S-M-z - Wiederholen (“Redo”) C-g - Escape S-M-f - Vollbildmodus ein-/ausschalten S-M-b - Buttons ein-/ausschalten S-M-l - Protokollfenster ein-/ausschalten S-M-m - Dunkle Benutzeroberfläche ein-/ausschalten S-M-s - Inhalt des Puffers in Datei speichern S-M-o - Datei in einen Puffer laden


10.3 - Teilen

In Sonic Pi geht es vor allen Dingen um das Teilen und gemeinsame Lernen.

Sobald Du gelernt hast, Musik zu programmieren, kannst Du Deine Kompositionen ganz leicht mit anderen teilen: Schicke Deinen Code einfach mit einer Email an Deine Freunde. Ich möchte Dich ermuntern, Deinen Code mit anderen zu teilen, so dass sie von Deiner Arbeit lernen und Teile davon in neuen Mash-Ups verwenden können.

Du hast keine Ahnung, wo Du Deine Werke am besten mit anderen teilen kannst? Ich empfehle Dir für Deinen Code GitHub und für Deine Musik SoundCloud. So erreichst Du schnell ein großes Publikum.

Code -> GitHub

GitHub wird von professionellen Entwicklern und auch Künstlern verwendet, um Code zu teilen und zusammenzuarbeiten. Auf GitHub gibt es einen eigenen Bereich namens Gist, damit kann man besonders einfach ein neues Stück Code (egal ob fertig oder nicht) veröffentlichen. Mit einem Gist können dann auch andere Deinen Code übernehmen, kommentieren und verbessern.

Audio -> SoundCloud

Statt Code kannst Du auch eine Musikaufnahme veröffentlichen und dafür eine Audiodatei zu SoundCloud hochladen. Dort können andere Nutzer Kommentare dazu abgeben und die Musik diskutieren. Es ist empfehlenswert, bei einer Veröffentlichung auf Soundcloud zusätzlich einen Link zu einem Gist mit Deinen Code in die Beschreibung aufzunehmen.

Um Dein Stück aufzunehmen, drücke den Aufnehmen-Knopf in der Werkzeugleiste, die Aufnahme startet dann sofort. Drücke Ausführen, um Deinen Code zu starten, wenn er nicht bereits läuft. Wenn Du fertig bist, drücke den den blinkenden Aufnehmen-Knopf erneut. Du wirst dann nach einem Dateinamen gefragt, unter dem die Aufnahme als WAV-Datei gespeichert wird. Es gibt viele Werkzeuge, um eine WAV-Datei in ein MP3 zu wandeln, z.B. das Programm Audacity.

Hoffnung

Ich möchte Dich ermuntern, Deine Arbeiten zu teilen, und hoffe sehr, dass wir uns gegenseitig neue Sonic-Pi-Tipps und -Tricks beibringen werden. Ich bin schon sehr gespannt darauf, was Du mir zeigen wirst.


10.4 - Auftritte

Einer der aufregendsten Aspekte von Sonic Pi ist, dass Du von nun an Code als Musikinstrument verwenden kannst. Vor Publikum zu programmieren wird damit ein neuer Weg, um Musik aufzuführen.

Das nennen wir Live-Coding.

Zeig Deinen Bildschirm

Wenn Du live programmierst, empfehle ich Dir, Deinem Publikum Deinen Bildschirm zu zeigen. Beim Gitarrespielen versteckst Du ja auch nicht Deine Finger und die Saiten Deines Instruments. Wenn ich zu Hause übe, verwende ich einen Raspberry Pi und projiziere den Bildschirm mit einem kleinen Beamer an meine Wohnzimmerwand. Du könntest auch Deinen Fernseher oder einen Projektor in der Schule/Arbeit verwenden und eine kleine Show geben. Versuch es, das macht eine Menge Spaß.

Gründe eine Band

Don’t just play on your own - form a live coding band! It’s a lot of fun jamming with others. One person could do beats, another ambient background, etc. Use the live_audio functionality to combine code with traditional instruments such as a guitar or a microphone.

See what interesting combinations of sounds you can create with code.

TOPLAP

Live-Coding ist nicht wirklich neu - eine kleine Gruppe von Enthusiasten macht das schon seit Jahren, typischerweise mit selbstgebauten, individuellen Systemen. Bei TOPLAP erfährst Du mehr über diese anderen Live-Coder und ihre Geräte.

Algorave

Einige Live-Coder spielen auch in Nightclubs. Mehr über dieses spezielle Genre erfährst Du auf Algorave.


- Minecraft Pi

Raspbian, das Linux-Betriebssystem für Raspberry Pi, bringt eine spezielle Version von Minecraft als Teil der Vorinstallation mit. Und mit Sonic Pi kannst Du über eine einfache API jetzt auch Minecraft Pi steuern.

Keine Bibliotheken notwendig

Die Verbindung zwischen Minecraft Pi und Sonic Pi ist so einfach zu nutzen, dass Du nichts dafür vorbereiten musst. Starte einfach Minecraft Pi und baue eine Welt. Du kannst sofort die mc_*-Funktionen in Deinem Code genau wie play oder synth einsetzen. Es gibt nichts zu installieren und keine Bibliotheken zu importieren - alles ist fix und fertig und sofort startklar für Dich.

Automatische Verbindung

Die Minecraft-Pi-API kümmert sich automatisch um die Verbindung zur laufenden Minecraft-Pi-Applikation. Du musst also nichts dafür tun. Sonic Pi wird sich allerdings freundlich beschweren, wenn Du die API benutzen möchtest und Minecraft Pi nicht gleichzeitig läuft. Und falls Du die Minecraft-Pi-Applikation beendest, während Dein Code die API in einem live_loop ansprechen will, wird der Loop beendet und Du erhältst eine entsprechende Fehlermeldung. Starte dann Minecraft Pi wieder neu, die API wird sich von selbst damit verbinden und Dein Code funktioniert wieder.

Gemacht für Live-Coding

Die Minecraft-Pi-API ist dazu da, dass sie problemlos in in einem live_loop funktioniert. Du kannst damit also Veränderungen in Deiner Minecraft-Welt mit Veränderungen in Deinen Sonic-Pi-Sounds synchronisieren. Zack, Minecraft-basierte Musik-Videos! Bedenke aber, dass Minecraft Pi eine Alpha-Version ist und die Software ein paar kleinere Bugs hat. Wenn ein solches Problem mit Minecraft Pi auftritt, starte es einfach neu. Sonic Pi nimmt dann automatisch eine neue Verbindung über seine API auf.

Raspberry Pi 2 empfohlen

Minecraft Pi und Sonic Pi mit seinen Sound-Möglichkeiten sind beides sehr rechenintensive Programme. Der alte Raspberry ist davon schnell überfordert und ein Raspberry Pi 2 mit seinem schnelleren Prozessor deshalb sehr zu empfehlen.

API-Support

Zur Zeit kann Sonic Pi einige Grundfunktionen zur Manipulation von Minecraft-Blöcken und dem Spieler ausführen. Details dazu findest Du im nächsten Abschnitt 11.1. Für ein späteres Release plane ich, auch Event-Callbacks aus Minecraft Pi zu unterstützen, so dass der Code in Sonic Pi von Ereignissen erfahren kann, die der Spieler in Minecraft Pi ausgelöst hat.


11.1 - Grundfunktionen der Minecraft-Pi-API

Folgende Aktionen kannst Du mit der API von Sonic Pi aus in Minecraft Pi auslösen:

Anzeige von Chat-Nachrichten die Spielerposition setzen die Spielerposition auslesen den Typ eines Minecraft-Blocks an einer Koordinate setzen den Typ eines Minecraft-Blocks an einer Koordinate auslesen

Schauen wir uns diese der Reihe nach an.

Anzeige von Chat-Nachrichten

Ich zeige Dir mal, wie einfach es ist, von Sonic Pi aus die Kontrolle über Minecraft Pi zu übernehmen. Starte also zunächst Sonic Pi und Minecraft Pi und betrete eine Minecraft-Welt, in der Du herumlaufen kannst.

Jetzt schreibe folgenden Code in einem leeren Puffer von Sonic Pi :

mc_message "Hallo, hier ist Sonic Pi"

Führe den Code aus und Du wirst die Nachricht im Minecraft-Fenster aufblitzen sehen. Gratuliere, das ist Dein erster Minecraft-Code! War doch gar nicht schwer, oder?

die Spielerposition setzen

Und jetzt zaubern wir ein wenig. Teleportiere Dich einfach, wohin Du willst! Probiere das hier aus:

mc_teleport 50, 50, 50

Nach Klick auf Ausführen - Paff! - bist Du sofort an einem anderen Ort in Deiner Minecraft-Welt. Vermutlich irgendwie hoch oben in der Luft und Du bist aufs Land oder Wasser herunter gefallen. Was bedeuten diese Zahlen: 50, 50, 50? Das sind die Koordinaten der Zielposition für den Teleport. Probiere jetzt in Deinem Code ein paar Koordinaten aus, damit Du weißt, wie sie funktionieren. Koordinaten sind für Minecraft-Programme sehr sehr wichtig.

Koordinaten

Stelle Dir eine alte Piraten-Schatzkarte vor - beim großen X ist der Schatz vergraben. Die genaue Position des X lässt sich mit zwei Zahlen beschreiben - wie weit ist das Kreuz horizontal vom Papierrand entfernt und wie weit vertikal. Angenommen, es ist 10cm vom linken Rand und 8cm vom unteren Rand entfernt. Diese beiden Zahlen 10 und 8 sind Koordinaten. Auch andere vergrabene Schätze auf der Karte lassen sich mit ihrem jeweiligen Koordinaten-Zahlenpaar genau beschreiben. Etwa die große Goldkiste bei 2 von links und 9 von unten…

In Minecraft Pi reichen zwei Zahlen nicht mehr aus. Wir brauchen auch eine Höhenangabe und damit besteht eine Koordinate in Minecraft aus drei Zahlen:

Wie weit von rechts nach links innerhalb der Welt - x Wie weit von vorn nach hinten innerhalb der Welt - z Wie hoch vom Boden aus innerhalb der Welt - y

Diese Minecraft-Koordinaten schreiben wir dann in der Reihenfolge x, y, z auf.

Finde deine aktuellen Koordinaten

Spielen wir also ein wenig mit den Koordinaten herum. Bewege Dich in Minecraft Pi an einen schönen Ort und wechsele dann zu Sonic Pi. Nun führe folgenden Code aus:

puts mc_location

Drücke Ausführen und im Log erscheinen die Koordinaten, an denen Du Dich in Minecraft befindest. Merke sie Dir, bewege Dich in Minecraft ein wenig weiter und führe den Code noch einmal aus. Hast Du gesehen, wie sich die Koordinaten geändert haben? Probiere das noch ein bisschen aus, damit Du ein Gefühl dafür bekommst, wie sich die Koordinaten mit Deiner Bewegung in der Minecraft-Welt verändern. Wenn Du erst einmal verstanden hast, wie Koordinaten funktionieren, wird das Programmieren mit der Minecraft-Pi-API ganz einfach.

Lass uns etwas bauen!

Jetzt weißt Du, wie Du Deine Position auslesen und Dich an beliebige Stellen teleportieren kannst. Als nächstes können wir mit Code Dinge in Minecraft bauen. Zum Beispiel ein Block aus Glas an Koordinate 40, 50, 60, das geht ganz einfach:

mc_set_block :glass, 40, 50, 60

Haha, das war ja wirklich ganz einfach. Nach getaner Arbeit springst Du schnell in die Nähe und schaust Dir das Ergebnis an:

mc_teleport 35, 50, 60

Dreh Dich ein wenig und Du wirst Deinen Glasblock sehen! Ändern wir kurz das Material und machen einen Block aus Diamant daraus:

mc_set_block :diamond, 40, 50, 60

Wenn Du in die richtige Richtung schaust, konntest Du eben sogar dabei zusehen, wie sich der Block verändert. Hier geschieht etwas aufregendes…

Blöcke ansehen

Du kannst Minecraft Pi auch fragen, welcher Typ Block sich an einer Koordinate befindet. Der folgende Code tut dies für den Diamantblock, den Du gerade gebaut hast:

puts mc_get_block 40, 50, 60

Yay! Es ist :diamond. Ändere das Material des Blocks wieder zu Glas und lese den Typ noch einmal aus - meldet der Code jetzt :glass zurück? Ich bin sicher, dass er das tut. :-)

Mögliche Materialtypen in Minecraft

Bevor Du jetzt mit Code durch Minecraft Pi tobst, hier noch eine praktische Liste aller Material-Typen für Minecraft-Blöcke:

    :air
    :stone
    :grass
    :dirt
    :cobblestone
    :wood_plank
    :sapling
    :bedrock
    :water_flowing
    :water
    :water_stationary
    :lava_flowing
    :lava
    :lava_stationary
    :sand
    :gravel
    :gold_ore
    :iron_ore
    :coal_ore
    :wood
    :leaves
    :glass
    :lapis
    :lapis_lazuli_block
    :sandstone
    :bed
    :cobweb
    :grass_tall
    :flower_yellow
    :flower_cyan
    :mushroom_brown
    :mushroom_red
    :gold_block
    :gold
    :iron_block
    :iron
    :stone_slab_double
    :stone_slab
    :brick
    :brick_block
    :tnt
    :bookshelf
    :moss_stone
    :obsidian
    :torch
    :fire
    :stairs_wood
    :chest
    :diamond_ore
    :diamond_block
    :diamond
    :crafting_table
    :farmland
    :furnace_inactive
    :furnace_active
    :door_wood
    :ladder
    :stairs_cobblestone
    :door_iron
    :redstone_ore
    :snow
    :ice
    :snow_block
    :cactus
    :clay
    :sugar_cane
    :fence
    :glowstone_block
    :bedrock_invisible
    :stone_brick
    :glass_pane
    :melon
    :fence_gate
    :glowing_obsidian
    :nether_reactor_core