Wie bereits im letzten Teil angekündigt will ich heute ein wenig die interne Arbeitsweise von Csound beleuchten und das ein oder andere nochmal wiederholen. Es könnte also vielleicht etwas trocken werden … 😉

Csound hält die Trennung von Instrument und Noten strikt ein. Eine Sammlung von Instrumenten nennt man ein Orchester (orchestra) und eine Sammlung von Noten (notes) nennt man Komposition (score). Beides wird in einer .csd Datei gespeichert (man könnte sie auch getrennt in zwei separate Dateien ablegen) und diese Datei ist direkt ausführbar.

Csound unterscheidet zwei unterschiedliche Sampleraten. Die eine ist für Audio Signale (sample rate, sr) und die andere für Kontrollsignale (control rates, kr). Ein typischer Wert für eine Control Rate wäre z.B. ein Zehntel 1/10 der Sample Rate.

Ausgehend vom Nyquist Gesetz für Audiosignale, muss die Sample Rate mindestens doppelt so hoch sein, wie die höchste Frequenz, die wir generieren wollen. Wenn wir also einen Sound mit 20.000Hz (höchste hörbare Frequenz für ein junges, gesundes menschliches Ohr) spielen wollen, muss die Sample Rate mindestens 40.000Hz betragen. Die Wahl der Sample Rate hat immer Einfluss auf die Qualität des hörbaren Audio Signals.

Kontrollsignale (wie bspw. Amplitude Envelopes, dynamisches Stereo Panning, etc…) können in viel kleineren Raten berechnet werden. Hierfür werden keine hohen Auflösungen benötigt. Ihre Wellenformen sind längst nicht so detailreich, wie die eines Audiosignals. Kleinere Auflösungen benötigen logischerweise auch nicht so viel Berechnungen und belasten den Rechner nicht unnötig.

Wenn eine .csd Datei kompiliert und interpretiert (übersetzt und ausgeführt) wird, führt Csound zunächst einige Vorberechnungen durch, bevor die Daten des Scores verarbeitet werden. Als erstes werden die benötigten Funktionstabellen berechnet und dann werden die Noten gelesen. Wird eine Note generiert, wird zunächst ein Initialisierungsschritt ausgeführt, um Berechnungen durchzuführen, die nur einmal am Anfang abgearbeitet werden müssen (pch in Hz Umwandlungen, dB in absolute Werte umwandeln, usw…). Sobald diese Sachen abgearbeitet wurden, werden Audiosignale generiert.

Konstanten und Variablen

In Csound gibt es einige konstante Werte, die während des Synthese-Prozesses unverändert bleiben. Genauso gibt es einige Variablen, oder Speicherbereiche, die mithilfe ihres Namens identifiziert werden, die sich im Laufe des Programms ändern können. Einige Beispiele für vordefinierte Konstanten sind z.B. die Sampling Rate für Audiosignale (sr), die Sampling Rate für Kontrollsignale (kr), die Zahl der ausgegebenen Kanäle, die entweder 1 (mono), 2 (stereo) oder 4 (quad) betragen kann.

Es gibt drei Arten von Variablen:

  • Initialization variables: ihr Name muss mit eine i beginnen. Sie werden nur einmal am Anfang für jede Note – am Beginn dieser Note -berechnet. Zulässige Namen wären bspw: i1, ifreq, istart, iend, i121, etc.
  • Control variables: ihr Name muss mit einem k beginnen. Sie werden in k-Raten berechnet. Zulässige Namen wären bspw: k1, kfreq, kstart, k444, etc.
  • Audio variables: ihr Name muss mit einem a beginnen. Sie werden in Sampling-Raten berechnet. Zulässige Namen wären bspw: a1, aout, astart, aend, a99, etc.

All diese Variablen werden immer in einem bestimmten Instrument definiert und sie sind auch nur in diesem gültig. Es gibt nur eine Möglichkeit Variablen zu definieren, die für alle Instrumente gültig sind, und zwar mit globalen Variablen. Der Name einer globalen Variable muss mit ein g beginnen. Da diese Variablen global sind, gelten sie für alle Instrumente eines Orchesters.

Nochmal zur Übersicht, wir haben:

initialization variables i…
control variables k…
audio variables a…
global initialization variables gi…
global control variables gk…
global audio variables ga…

Die Csound Syntax

Orchester

Csound ist eine wirklich mächtige Synthese Programmiersprache. Trotzdem muss der User einige Grundsätze beachten und befolgen, um das volle Potential zu nutzen. Die Instrumenten-Sektion <CsInstruments> beginnt mit dem Header, wo sr, kr, das sr/kr ratio und nchnls festgelegt werden. Ein Beispielheader:

sr     = 20000    ; audio sampling rate
kr     = 1000     ; control rate
ksmps  = 20       ; = sr/kr
nchnls = 2        ; number of output channels

Alles, was hinter einem Semikolon geschrieben wird, ist ein Kommentar und wird vom Interpreter schlichtweg ignoriert, hat also keinen Einfluss auf das Programm. Nach dem Header kommt das eigentliche Orchester, sprich die Instrumente. Ein Instrument muss zwischen instr und endin beschrieben werden.

instr 1
...
...
...
endin
instr 2
...
...
...
endin

Die allgemeine Form eines Orchester-Statements sieht so aus:

[label:] [result] opcode [arg1, arg2, arg3 …]

Eine eckige Klammer zeigt an, dass ein Argument optional ist. Das hängt immer vom verwendeten Opcode ab. Aus diesem Grund benötigt man label, result oder arguments für einige Opcodes nicht.

Csound Anweisungen werden logischerweise von rechts nach links abgearbeitet, deshalb würden wir hierfür:

2 + 3 = 5

Folgendes schreiben:

5 + 2, 3 wenn + ein Opcode wäre, mit den Argumenten 2 und 3

Ein Opcode ist ein Symbol, dass für eine bestimmte Programmeinheit steht. Einige Beispiele machen das deutlich:

a1    oscil    iamp, ifreq, itab

ruft einen oscil Generator (einen Oszillator) auf. Dieser Oszillator speichert sein Resultat in einer Audiovariablen mit dem Namen a1 und benutzt die Argumente iamp (Amplitude), ifreq (Frequenz) und itab (ID einer function table).

out  a1

ruft den Opcode out (Output) auf und benötigt nur ein Input Argument. Dieser Opcode hat keine Output Variable, sondern gibt Audio direkt ans System weiter (oder speichert das Signal in eine vorher angegebene Datei).

ifreq  =  cpspch(8.09)

ruft eine Konvertierung von Tonhöhe nach Hz auf. Das Resultat dieser Konvertierung (440Hz) wird in der Variablen ifreq gespeichert.

Syntaxfehler werden von Csound während der Kompilierung erkannt und es wird eine Nachricht ausgegeben, die die Zeile angibt, in der der Fehler erkannt wurde.

Komposition

Im Score <CsScore> ist ein Opcode jeweils das erste Zeichen pro Zeile. Score Opcodes sind : i, f, a, t, s und e. Die wichtigsten Opcodes sind i (Note) und f (Funktion oder Wavetable). Eingabeparameter werden mit Leerzeichen getrennt und stehen hinter den Opcodes. Die Anzahl der Parameter kann variieren.

Hier sind zwei typische Zeilen eines Csound Scores:

f1  0  4096  10  1  .5
i1  9  1.5  80  6.078  1  3  6  .66

Eine kurze Geschichte der Sound Synthese Programmiersprachen

Sound Synthese im Computer wurde gegen Ende der 50er Jahre des letzten Jahrhunderts geboren. Die ersten Systeme waren MAESTRO und die Serie der MUSIC Sprachen, entwickelt von Max V. Mathews in den Bell Laboratories. 1965 hat Hubert Howe dann MUSIC4BF in Fortran entwickelt. 1966 wurde MUSIC10 von John Chowning geschrieben. Drei Jahre später, 1969 wurde dann MUSIC360 von Barry Vercoe geboren und dann auch die Nachkommen MUSIC11 und Csound.

Nahezu alle diese Sprachen nutzten die Trennung von Orchestra und Score und somit auch die Note als kleinste Einheit. Das Problem bei den älteren Systemen vor Csound war, dass sie nur auf dem Rechner liefen, auf dem sie auch entwickelt wurden. Der enorme Erfolg der Programmiersprache C erklärt, warum Csound (welches in C geschrieben wurde) sich so rasant verbreiten konnte. Heutzutage kann Csound auf jedem Rechner installiert werden.

Max Mathews, John Chowning & Curtis Roads – Music Meets The Computer

In den 60ern gab es noch keine Personal Computer, die man sich auf den Schreibtisch stellen konnte (damals glaubte man sogar noch, dass die riesigen Rechner niemals auf die heutige Größe schrumpfen würden). Die Programmierer verrichteten ihre Arbeit in Rechenzentren diverser Universitäten, wo man sich einen Hauptrechner teilte, der längst nicht so leistungsstark war, wie heutige Laptops.

Jeder Programmierer hatte nur ein kleines Kontingent an Rechenzeit. Meistens wurde am Abend ein fertiges Programm kompiliert und man konnte erst am nächsten Tag kontrollieren, ob alles funktioniert. Fehlerkorrektur dauerte somit Tage oder Wochen. Damals wurden dann auch die ersten hybriden Systeme gebaut. In diesen Systemen wurden alle Kontrolldaten auf einem Minicomputer berechnet, während die Synthese auf einem analogen System durchgeführt wurde (Moog, Arp, Buchla oder EMS).

1982 wurde dann ein Standardprotokoll namens MIDI ins Leben gerufen. Damit konnten verschiedene elektronische Instrumente miteinander kommunizieren. Zu der Zeit kamen auch die ersten Personal Computer auf den Markt und Synthesizer wie der Yamaha DX7 wurden populär. Aber die kompakten Synthesizer hatten natürlich ihre Limits. Modulare Systeme waren da schon flexibler, aber viel zu teuer. Dank steigender Rechenleistung der Computer wurden musikalische Programmiersprachen wie Csound immer populärer und gaben dem Komponisten schier unendliche Möglichkeiten…

Im nächsten Teil wird es dann wieder etwas spannender und wir werfen einen Blick auf die additive Synthese.