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.