MuSys - Werkzeug der Komposition

Rainer Nowotny
Rainer Nowotny

03.07.2017
Was ist "Künstliche Intelligenz"? Einfach nur große Datenmengen auswerten? Oder Züge des menschlichen Denkens algorithmisieren?

1988 stellten wir uns die Aufgabe, ein musikalisches Kalkül zu entwickeln, mit dem die formalen Gesetze der Musiktheorie formuliert und mit dem Kompositionsvorschriften programmiert werden können.
Wir, das waren Andreas Kortke, Martin Rosengarten und meine Person, Rainer Nowotny.

Wir erweiterten "C" für musiktheoretische Sprachimplementierung, schrieben Musikbibliotheken und entwickelten erste Kompositions-Software.
Zum Zwecke einer Vermarktung entwickelten wird 1990-92 eine Hardware auf Basis eines 68340-Prozessors und entwickelten ein eigenes Betriebssystem mit Schwerpunkt Musik-Schnittstellen.

Der Entwicklung stand die Frage nahe: Gibt es einen Sprung vom Syntax zur Semantik?
Ganz klar: Es gibt keinen Übergang von Sprachlogik zum Sprachinhalt.
Intelligenz bedarf zur Kommunikation der Sprache, insbesondere einer logischen Sprache.
Jedoch kann aus der Weiterentwicklung einer Sprache niemals ein intelligentes System entstehen. Wissensbasen und das Würfeln auf einer Auswahlmannigfaltigkeit ist keine Intelligenz, sondern Syntax und bleibt auch Syntax.
MuSys ist nicht das syntaktische System, einer Rechenmaschine das Komponieren beizubringen, sondern es ist das syntaktische Werkzeug, die Gedanken des Komponierens syntaktisch zu formulieren. Mit MuSys werden ästhetische Aussagen in eine formale Sprache übertragen, hierzu erweiterten wir "C".
MuSys war die Übertragung von der unscharfen Sprache der Musiktheorie in einen formalen Syntax.
Dieser Spracherweiterung verschafften wir 1988-1992 eine eigene Hardware. Bühnengerecht war es in einem 19'' Rack untergebracht. Als Masse-Speicher nutzen wir die damals völlig neuen Flash-Speicher.

Zu diesem Zeitpunkt waren die Rechnerarchitekturen bereits vollständig im Sinne des Rechnenden Zuse-Raumes entwickelt. ANSI-C, UNIX-kompatibel, war vollständig entwickelt; Klassenkonzepte wurden bereits implementiert ; C++ war gerade am Entstehen (etwa 1982-1992).
Die erweiterten Möglichkeiten der objektorientierten C++-Progarmmierung waren für die Entwicklung der Bibliotheken von MuSys vorerst nicht zielführend.

Unsere Arbeit basierte auf der Arbeit zur Harmonie über einer diskreten Menge.

Zunächst musste eine Notation zugrunde gelegt werden, mit der die Musiktheorie korrespondiert, dazu sind als erstes gebrochene Zahlen nötig, Gleitkomma- oder Fließkomma-Zahlen sind völlig unbrauchbar. Also galt es als erstes, die Bruchrechnung in "C" zu integrieren.
Mit der zumeist sehr einfachen Bruchrechnung ist das arithmetische Rechnen in der Musiktheorie auch schon beendet. Alles höhere Rechnen unter MuSys unterliegt semanischen Bestrebungen.

Die Musiktheorie ist unbestritten ein Kalkül, welches mithilfe der MuSys-Bibliotheken die oben genannte Harmonietheorie interpretiert.

Mit der Zurverfügungstellung von musikalischen Bibliotheken geht es um den Sprung von der Aussagenlogik hin zu einer höheren logischen Formalistik, der musikalisch logischen Sprachwelt, der ästhetische Axiome oder stilistische Gesetzmäßigkeiten zugrunde gelegt werden können.



25 Jahre MuSys-Player

Just als diese Bibliotheken, um Tonarten und tonale Melodien, Akkorde und akkordische Figurationen, rhythmische Phrasen und so weiter beschreiben zu können, implementiert waren, fielen quasi über Nacht die politischen Differenzen und damit auch die Handelsbeschränkungen. Wir ergiffen die Chance, entwickelten eine eigene Rechnerhardware (im Zentrum ein Motorola 68.340), ein Betriebssystem (an UNIX angelehnt) und ein Filesystem, ausschließlich mit Schnittstellen (i) zu Sounderzeugern oder ähnlichem, (ii) zu inzwischen externen etablierten Kompositionseditoren und natürlich (iii) zu externen Programmierebenen. Einen Monitor brauchten wir nicht, nur ein kleines Displays. Auch unsere Tastatur erinnerte mehr an einen Industriecomputer.
Beim Versuch der Vermarktung dieses MuSys-Players stießen wir Unverständnis, sich solchen logischen Werkzeugen hinzugeben.
Letztendlich wurden bis 1992 nur 4 Exemplare des MuSys-Player gebaut.
Heute, 25 Jahre später, sehen wir, dass viele Ideen unserer Entwicklung ansatzweise bereits musikalische Praxis wurden.



Der Ansatz kurz gefasst

Die Darlegung von Motiven, Figurationen, Phrasen etc. als abstrakte Einheiten der Informatik in Form von Vorschriften, die nach ästhetischen Grundsätzen bearbeitet, modifiziert und ausgeweitet werden können und zu denen Gegenstimmen und Begleitsequenzen erstellt werden können.
Dur- und mol- Melodien sind demnach Folgen von Zahlen innerhalb der Dur- oder mol-Auswahl, speziell dargestellt als Wert mod 7 und Oktavwert und eines Zähler/ Nenner/ Bruches, vorzugsweise mit Nenner: 4, 8, 16, 32 etc. Ggf 3, 6 etc. Die Canturfirmusgesetzte nach Höhepunkt, Auflösung, Verbot von Dopplung etc, lassen sich hier sehr einfach mit Zahlen durchspielen. So ist eine Quarte +3 und die Quarte auf x ist (x+4) mod 7, oder auch (x+4)%7, denn in C-Dur ist eine Quarte auf c (0) ein (0+3)%7 = 3 also f, aber eine Quarte auf a (5) ist (5+3)%7 = 1 also ein d. Wobei wir ja wissen, dass in Dur der Sprung von ()%7 = 3 auf ()%7 = 6 eine ü4 darstellt, der vielleicht verboten werden kann. Terzlinien sind hingegen ()+2,()+2 etc. Aber natürlich stets mod 7. Die Oktave ist: oktave(x+y)= oktave(x) + ((x+y)/7).
Genre-spezifische Melodien wie abwärzgerichtete Terzlinien, Auflösungen von Höhepunkten usw. lassen sich hier formulieren.

Ähnlich verhält es sich in Akkorden. Ein Arpeggio ist demnach eine schnelle Akkordtonfolge +1 +1 +1 etc. je natürlich mod n // n ist die Anzahl der Akkordtöne. Die Oktave ist dabei: oktave(x+1)=oktave(x) + ((x+1)/n).
Akkorde sind in diesem System die Grundlage für die Mehrstimmigkeit, sodass Polyphonie z.B. den ü4 verbieten kann, die Verdopplung der Terz im Bass im Bass und auch sonst Genre-spezifische Eigenarten gut formuliert werden können.
Genau wie die Notenfolge durch die Melodie in einer Tonart wird auch die Notenfolge über einem Akkord durch Notenelement mit dem Wert "p" (Pause) ergänzt. Diese Einbeziehung der Pause unterliegt wie vorher die gesamte Erstellung den ästhetisch motivierten oder erdachten Vorgaben des Programmierers.

Die Abfolge von Tonarten oder Akkordschemen machen nun den Syntax zur Rechenaufgabe, wobei gerade in den Tonartfolgen und den Akkordfolgen das Zusammenspiel beider beachtenswert ist und Gerne-spezifisch formuliert werden muss.
Erweitert wurde dieser Zusammenhang noch durch ein Element der Phrase, welches wieder ein Überelement der Phrasenfolge zugeornet ist; auch hier Genre-spezifisch das Zusammenwirken mit Tonartfolge und Akkordfolge.


Heute wird klar, dass wir der Zeit doch einige Jahrzehnte voraus waren.


Der nachstehende Fachartikel "Kompositionscomputer"erschien 1990 in der Zeitschrift "Wissenschaft und Fortschritt" .
Ihm folgt eine Kurzreferenz des Betriebssystems (1991) und der Schaltplan der Hardware (1992).


A. Kortke Berlin, den 4. 3.1991

MuSys ist die informatische Umsetzung der algebraischen Musiktheorie von R. Nowotny. Die darin enthaltenen Konstruktionen sind als Datenstrukturen und Funktionen in der Programmiersprache "C" ausgeführt. Nachfolgend wird das Programmsystem strukturell und inhaltlich beschrieben.

Das System gliedert sich in folgende Programmdateien:

MusicDef.h - Definition grundlegender Konstanten und Datenstrukturen, sowie Deklaration systemübergreifender Variablen.

MuSys.c - enthält Funktionen für die Behandlung von Noten, Notenfolgen und Motiven.

Brueche.c - enthält Funktionen für das Rechnen mit "echten Brüchen" und arithmetische Hilfsmittel.

Tonarten.c - enthält Funktionen für die Arbeit mit Tonarten, Tonartfolge, Tonartverwandtschaften und Motiven.

MusicIo.c - enthält Funktionen für die Ein- und Ausgabe von Notenfolgen und Musikstücken.

Oktaven.c - Behandlung von Oktaven.

Phrasen.c - Behandlung rhythmischer Phrasen und Folgen rhythmischer Phrasen.

Skalen.c - Funktionen zur Behandlung der Skalenlehre, sowie zur Arbeit mit Akkorden, Akkordfolgen und Figuration.

Stilist.c - genrespezifische Funktionen zur Beachtung musikästhetischer Grundsätze.

Analyse.c - beinhaltet eine Test- und Analysefunktion zur Begutachtung von Zwischenergebnissen.

Demonstraton:
Vierstim.c - komponiert einen vierstimmigen Satz.
Fuge.c - komponiert eine zweistimmige Fuge in strenger Form.
JazzPar.c - komponiert zweistimmige Jazz- Etüden.
Cantus.c - komponiert einen "cantus firmus".

Weiterhin existieren verschiedene Datenstrukturen - "musikalische Elemente".
Alle Elemente der Musiktheorie sind in speziellen C-Variablen, Datenstrukturen oder Funktionen realisiert.

1. Noten:

Zur Darstellung von Noten ist in "MusicDef.h" die Struktur "Note" deklariert. Sie enthält eine weitere Struktur "Br". Mit einer Struktur "Br" lässt sich ein echter Bruch aus Zähler- und Nennerkomponente darstellen. In der Struktur "Note" repräsentiert der Bruch den Notenwert. Weiter enthält die Notenstruktur ein Element "N" für die Angabe der Tonhöhe (Nummer des Halbtons: 0 entspricht c) und das Element "O", die Oktavnummer der Note.
Diese drei Elemente beschreiben eine einzelne Note vollständig.

2.Notenfolgen:

Um nun Sequenzen von Noten (Melodien u.s.w.) bilden zu können ist eine weitere C- Konstruktion erforderlich. Es handelt sich um eine Struktur, die man allgemein als Kettenglied bezeichnen kann. Diese Struktur "Notenfolge" enthält drei Zeiger auf Objekte des selben Typs, zwei "short" Elemente und ein Feld von Notenstrukturen, dessen Elementanzahl durch den Konstantwert "SEG" bestimmt wird.
Diese Art der Verkettung von Einzelelementen oder Segmenten (Feldern) von Einzelelementen wird im System noch wiederholt auftreten. Die im Kettenglied (Struktur Notenfolge) enthaltenen Zeigerelemente zeigen auf das vorhergehende Kettenglied (*Prev), das nachfolgende Kettenglied (*Next) und das letzte Kettenglied (*Last). Der Zeiger "*Last" hat nur im ersten Kettenglied einen
gültigen Wert. Im ersten Kettenglied hat "*Prev" und im letzten Glied "*Next" den Wert Null.
In einer eingliedrigen Kette ist *Next und *Prev gleich Null und *Last zeigt auf dieses, erste Glied. Operiert wird mit solchen Ketten immer durch den Zeiger auf das erste Glied, in unserem Fall: "struct Notenfolge *". Das "short" Element "First" der Struktur Notenfolge gibt die Nummer der 1. Note des Kettengliedes
innerhalb der Kette an. Im ersten Kettenglied hat es den Wert Null.
Das Element "Len" gibt die Anzahl der im Kettenglied gültigen Noteneintragungen an. Die Addition von "First" und "Len" des letzten Kettengliedes ergibt die Anzahl der in der Kette vorhandenen Elemente (Noten). Für die Arbeit mit Notenfolge existieren in der Programmdatei "MuSys.c" einige Grundfunktionen,
die sinngemäß bei allen Kettentypen (Tonartfolge, Phrasenfolge u.s.w.) Anwendung finden.

1. OpenNf() Öffnen einer Notenfolgen-Kette.
Ist der Übergabeparameter gleich Null, so wird eine neue Notenfolgenkette eingerichtet und ein Zeiger auf diese zurückgegeben. Ist der Parameter ein gültiger Zeiger auf eine Notenfolgen-Struktur, so wird diese um ein Glied verlängert. Im Fehlerfall wird ein Nullpointer zurück gegeben.

2. CloseNf() Schließt die spezifizierte Notenfolge. Speicher wird freigegeben, die Notenfolge existiert nach Aufruf dieser Funktion nicht mehr!

3. NfSegment() Systeminterne Funktion, vom Anwender normalerweise nicht benötigt. Sie liefert den Zeiger des Kettengliedes der spezifizierten Note.

4. AddNote() An die spezifizierte Notenfolge wird eine Note angefügt. Sollte kein Platz im letzten Kettenglied sein, so wird die Kette um ein Glied verlängert. Normalerweise wird der Zeiger auf die Notenstruktur zurückgegeben, in die eine neue Note eingetragen wurde. Im Fehlerfall wird ein Nullpointer zurückgegeben.

5. LastNote() Liefert die Anzahl der Noten in einer Notenfolge.

6. GetNote() Liefert den Zeiger auf die spezifizierte Note der Notenfolge, im Fehlerfall einen Nullpointer.

7. PutNote() Überschreibt die spezifizierte Note der Notenfolge mit den Werten der übergebenen Notenstruktur. Existiert die spezifizierte Note nicht in der Notenfolge, so wird diese entsprechend verlängert.


3. Scores

Komplette Musikstücke werden durch die Datenstruktur "Score" dargestellt. Diese Struktur ist ebenfalls ein Kettenglied, das heißt, sie enthält die nötigen Zeigerelemente. Jedes Kettenglied stellt eine zeitlich verzögerte Melodie, oder eine Stimme einer Polyphonie dar. Hierzu enthält jede Struktur "Score" einen Zeiger auf eine Notenfolge, die Struktur eines Bruches als zeitlichen Anfangsoffset und eine Tonartstruktur "Haupttonart". Des weiteren existieren die Variablen "Tempo" (Spieltempo [0...255]), "MidiKanal" x*[1...16] für Midiausgabe, "Attack" - Anschlagzeit [0...127], "Release"- Abklingzeit [0...127], "Volume"- Gesamtlautstärke [0...255]. Für den Umgang mit Score- Strukturen existieren die Funktionen:

1. AddScore() Eröffnen oder Erweitern einer Score- Kette.

2. CloseScore() Schließen einer Score- Kette.

Insbesondere bei Ein-/Ausgabefunktionen sind die Parameter vom Typ "struct Score *".


4.Tonarten

Ähnlich wie für Noten existieren Datenstrukturen für Tonarten und Tonartfolgen. Die Elemente einer Tonartstruktur sind vier Variable: "Gt"- Grundton (c,cis,...,h) und "Tg"- Tongeschlecht (Dur,moll,...).
Der Grundton ist analog zu den entsprechenden Noten codiert. Tonartverwandtschaften sind als Funktionen mit Operanden vom Typ "struct Tonart" in "Tonarten.c" definiert. Die Komponenten "Ia" und "Ie" sind Indizes, die den Abschnitt in einer korrespondierenden Notenfolge bestimmen, in dem diese Tonart gültig ist. Der Index "Ie" zeigt jedoch nicht auf die letzte Note der jeweiligen
Tonart, sondern auf die erste Note der nächsten Tonart. So ist "Ie" einer Tonart gleich "Ia" der nächsten Tonart. Das hat Konsequenzen bei der Gestaltung von Programmschleifen und Abbruchbedingungen.
Eine Tonartfolge- Struktur beinhaltet wie die Notenfolge- Struktur auch, drei Zeigervariablen und zwei "short" Variablen "First" und "Len", sowie ein Feld von Tonartstrukturen mit "SEG" Elementen. Für den Umgang mit Tonartstrukturen und Tonartfolgen existieren in "Tonarten.c" folgende Funktionen, deren Funktion sinngemäß den Funktionen für Noten und Notenfolgen entspricht: OpenTf(),
CloseTf(), TfSegment(), AddTonart(), GetTonart(), LastTonart() u.a.
Weiterhin gibt es Funktionen für die Tonartanalyse von Notenfolgen (Tonarten(), Tonartzahl(), GetTonartTabelle(), Rnd_Tonart(), Tonart1(), Tonart2(), Tonartanalyse() u.a. ), sowie für die Arbeit mit Tonartverwandtschaften (RndTonartverwandtschaft(), Verwandtonart(), Verwandtschaft()) und die Funktion GetTonartI(), die die Tonart zu einem bestimmten Notenindex liefert, u.a.).
Die Funktion "Leiterton()" liefert die Leitertonnummer einer Note bei gegebener Tonart, die Funktion "Note()" liefert zu gegebenem Leiterton und Tonart die entsprechende Note. Diese Funktionen benutzen das zweidimensionale integer- Feld "TonartSteps[][]", welches zu jedem Tongeschlecht die Folge der Halbtonschritte
enthält, um die Tonleitern korrekt zu bilden. In das zweidimensionale integer- Feld "TonartTest[][]" werden, durch einen Aufruf der Funktion "Tonarten()", die für einen Notenfolgenabschnitt in Frage kommenden Tonarten eingetragen. Das Feld enthält ein Element für jede Tonart. Hat ein Element einen Wert größer 0, so ist diese Tonart für den untersuchten Notenfolgenabschnitt möglich.
Aus den möglichen Tonarten kann dann mit weiteren Funktionen (Tonart1().Tonart2()...)eine ausgewählt werden. Die Funktion "GetTonartTabelle(n)" liefert die n.te gültige Tonart aus der Tabelle. Funktionen, deren Rückgabewert vom Typ "struct Tonart" ist, signalisieren einen Fehler als Rückgabe: Tonart- Grundton gleich "note_p".


5. Rhythmische Phrasen

Rhythmische Elemente stellen die "Phrasen" dar. Es handelt sich ganz allgemein um Folgen von Noten, bei denen die wesentlichen Informationen im Notenwert liegen und die nur den Zustand "Pause" oder "reguläre Note" kennen. In der Programmdatei "Phrasen.c" sind wieder die bekannten Funktionen: OpenPf(), ClosePf(), PfSegment(), AddPhrase(), GetPhrase(), LastPhrase(), PutPhrase(), GetPhraseI() u.a. realisiert. Weiterhin enthält sie eine Tabelle "Standard Phrasen" (StdPhrasen[]) mit vordefinierten Rhythmen, eine Tabelle für den Austausch von Phrasen (Kasten) und dazugehörige Funktionen (Kasten_Nr(), MakeSammelKasten(), NeuPhrase() und Phrasenaustausch() u.a.). Die Funktionen für die Phrasenanalyse von Notenfolgen heißen: Phrasentest() und Phrasenanalyse(). Außerdem existiert eine Funktion Motivwahl(), die zu einer gegebenen rhythmischen Phrase ein Motiv in der angegebenen Tonart wählt.


6. Oktaven

Die Programmdatei "Oktaven.c" enthält Funktionen für die Bestimmung von Oktavwerten nach einfachen Kriterien.


7. Ein-/Ausgabe von Notenfolgen und Musikstücken (Scores)

Für die Ein-/Ausgabe von Notenfolgen und Scores gibt es mehrere Möglichkeiten, die je nach Verwendung des Systems zur Anwendung kommen, bzw. noch um weitere Arten ergänzt werden müssen. Realisiert sind die Ein-/Ausgabe in einer unstandardisierten Textnotation (InMelodie(), PrMelodie(), LoadAscii() und SaveScore()) die folgendermaßen beschrieben ist: [n/m]N[O][, oder .]
wobei [n/m] ein Notenwert bedeutet, der, wenn er weggelassen wird gleich dem Notenwert der vorhergehenden Note bzw. "1/1" gesetzt wird. "N" repräsentiert die eigentliche Note als Textkürzel ("c", "cis","d","dis","e", "f", "fis", "g", "gis","a","b","h","p").
Schließlich [O] ein optionaler Oktavwert, der, wenn er weggelassen wird, gleich dem Oktavwert der vorhergehenden Note bzw. "1" gesetzt wird. Mehrere Noten werden durch Kommata getrennt, ein Punkt beendet die Notenfolge.
Eine weitere Ein-/Ausgabe Möglichkeit ist die Arbeit mit standardisierten Dateiformaten für Musikstücke. Dabei stehen je nach Rechnersystem unterschiedliche Formate zur Verfügung.
Je nach Verfügbarkeit einer Midi- Schnittstelle kann man ein Musikstück direkt über ein angeschlossenes Midi- Instrument spielen, die entsprechende Funktion heißt "Play()".


8. Test- und Analysefunktion

Die Programmdatei "Analyse.c" enthält eine Funktion "Analyse()", mit der es möglich ist, Notenfolgen, dazugehörige Tonart- und Rhythmusphrasenabschnitte in einer Datei abzulegen. Die Datei wird mit einem Filepointer spezifiziert.
Im Programm muss ein Filepointer "fp_prot" global deklariert sein und einer Datei zugewiesen werden (bzw. Nullpointer). In dieser Datei werden alle signifikanten Zwischenergebnisse abgelegt, um nach dem Programmlauf eventuell den Kompositionsvorgang zu überprüfen.


9. Kompositionsprogramme

Die oben beschriebenen Funktionen und Datenstrukturen sind die Grundlage für Programme die spezielle Kompositionen ausführen können. Bisher wurden die Programme "vierstimmiger Satz (Vierstim.c)", "zweistimmige Fuge in strenger Form (Fuge.c", "Jazzparnaß (Jazzpar.c, Skalen.c)" und "Cantus Firmus (Cantus.c)" realisiert.