Standard-MIDI-File in C - Teil 1

Was steckt hinter dem Standard-MIDI-File? In unserem MIDI-Seminar entschlüsseln wir das Universalformat und geben Tips für eigene C-Programme.

Ein Standard-MIDI-File enthält Daten im Hex-Format, die ohne Lücke aneinandergefügt sind. Es ist also nicht etwa so wie in Texten im ASCII-Format, in denen die Wörter durch Leer- oder Interpunktionszeichen getrennt sind, auch nicht so wie bei C-Quelltexten, wo die Anweisungen durch einen Strickpunkt, Parameter durch ein Komma usw. getrennt sind.

Um nun dennoch eine Struktur in die Daten zu bekommen, sind sie in Blöcken organisiert und innerhalb dieser in einer ganz bestimmten Reihenfolge angeordnet. Diese Blöcke sind mit Namen bezeichnet, die das Programm unbedingt erkennen muß, um von ihnen aus dem nachfolgenden Datenblock analysieren zu können. Der erste Datenblock heißt immer »MThd« Header-Block:

0x4D 0x54 0x68 0x64 ASCII: MThd

Es ist ein Dateikopf, der allgemeine Angaben über das gesamte Standard-MIDI-File enthält. Der zweite und ggf. alle weiteren Datenblöcke heißen »MTrk«.

0x4D 0x54 0x72 0x68 ASCII: MTrk

Track-Blöcke enthalten die eigentlichen Musikdaten. Dabei folgt stets auf ein Delta-Time ein MIDI- oder Meta-Event. Delta-Time ist der zeitliche Abstand zwischen zwei Ereignissen. Sie wird im »Format variabler Länge« dargestellt. Dies hat den Vorteil, daß nicht immer alle 8 Byte benötigt werden, sondern bei Zeiten bis zu 0x7F 1 Byte genügt. Somit besteht bis zu diesem Wert kein Unterschied zur üblichen Darstellungsform.

Zur Kennzeichnung der Hex-Daten verwenden wir dieselbe Kennzeichnung wie in C-Quelltexten, also 0x. Der ASCII-Code ist einfach eine Teilmenge aller Hex-Codes. Die 4 Byte, die den Header-Block kennzeichnen, sind ASCII-Code und werden demzufolge auch von jedem Texteditor korrekt dargestellt. Der Turbo-C-Editor zeigt auch alle anderen Hex-Codes korrekt an, allerdings ermöglicht er nicht deren Eingabe, da ja über die Tastatur nur ASCII-Zeichen eingegeben werden können. Hier hilft der Turbo-C-Debugger.

Unser erstes Beispiel »SMF_DEMO« benötigt zwei Funktionen, »ANALYSE« und »WIEDERGABE«. Damit das Programm korrekt beendet wird, plazieren wir noch den Schalter »BEENDEN«. Es genügt somit eine Dialogbox mit drei Buttons. Realisiert ist der Oberflächenteil von SMF_ DEMO im Modul SMF_GO.C (Listing A).

Das Modul SMF_GO.C schließt die Header-Datei AES.11, welche die hier verwendeten GEM-Routinen enthält, ein. Dann folgt die Hauptfunktion. In ihr wird als erstes mit der Funktion appl_init() die Applikation bei GEM angemeldet. Danach folgt noch eine spezielle Initialisierungsfunktion smf_init(), die alles enthält, was darüber hinaus zur Initialisierung benötigt wird. Das Standard-MIDI-File wird nur einmal geladen und dann im Arbeitsspeicher analysiert und wiedergegeben. Danach folgt eine Do-while-Schleife, die den sichtbaren Teil der grafischen Oberfläche, die Alert-Box, enthält und deren wiederholten Aufruf ermöglicht. Sie liefert als Rückgabewert die Nummer des aktivierten Buttons. Er wird der Funktion test(button) als Parameter übergeben. Bei Bedarf kann man mit derselben Bedienungsoberfläche mehrere SMF_DEMO-Programme anfertigen. Der wiederholte Aufruf der Anfangs-Alert-Box ermöglicht wiederholtes Testen einfach durch Drücken des betreffenden Knopfs. Dabei wird das Standard-MIDI-File nicht mehr neu geladen, sondern aus dem Arbeitsspeicher nur erneut abgearbeitet. Der Schalter BEENDEN bewirkt das Verlassen der Do-while-Schleife. Danach werden sowohl die anwendungspezifischen als auch die »AES«-üblichen Deinitialisierungen vorgenommen und das Programm beendet.

Nun zum Modul SMF_DEMO.C (Listing B). Nach dem Titel und Kommentar werden die benötigten Header-Dateien einbezogen. Danach folgt die Definition von SMFLEN, der Länge des Standard-MIDI-Files. In einer endgültigen Fassung würde man die Länge der zu ladenden Datei ermitteln.

Nun folgen globale Variablen. Hierbei handelt es sich um solche, zu denen der Zugriff von unterschiedlichen Stellen des Programms aus möglich sein soll. Dies gilt z.B. für »*speicher«, den Zeiger auf die Anfangsadresse des SMF-Speichers, der in vielen Funktionen benötigt wird. Entsprechendes gilt für buffer[STELLEN], ein Puffer, in dem die zu erkennenden Ausdrücke zusammengesetzt werden. Weitere Variable sind die aktuelle MIDI-Kanalzahl k, die aktuelle Dynamik dyn, die Variable on_off, die zwischen Ton-Ein- und -Ausschalten wechselt, die aktuelle Tonhöhe th und die Metronomzahl M, die hier auch gleich auf einen bestimmten Wert gesetzt wird.

Danach folgen die Funktionsprototypen. Hier schließen sich alle benötigten Funktionen an. Am Anfang steht die Funktion test(button), in der die beiden Funktionen analyze_smf() und play_smft() aufgerufen werden. Aus- und Einschalten der Maus besorgt die Betriebssystemfunktion graf_mouse(). Ohne diese Maßnahme würden Löcher entstehen, wenn die Maus bewegt wird.

Es folgt die Funktion init_smf(): Zum Initiieren des Standard-MIDI-Files wird der benötigte Speicher reserviert und mit Leerzeichen gelöscht. Die dazugehörige Funktion exit_smf() gibt den reservierten Speicher frei, bevor das Programm beendet wird.

Mit der Funktion load_smf() wird das Standard-MIDI-File komplett und unverändert in den Arbeitsspeicher geladen. Dies ist deshalb von Bedeutung, weil vielfach beim Laden bereits eine Umwandlung erfolgt, die für die spätere Wiedergabe erforderlich ist. Nicht so hier. Das Ganze ist ungeachtet des Demo-Charakters dieses Programms mit Fehlerüberprüfungen angereichert, denn diese sind ja nicht nur für den Anwender eine Hilfe, sondern auch für den Programmierer. So wird unbeschwertes Testen möglich.

In der nächsten Folge wollen wir schließlich bestimmte Aktionen auslösen.

(mn)

Das Modul »SMF_GO.C«Das Modul »SMF_GO.C«
Herbert Walz


Aus: ST-Magazin 10 / 1991, Seite 32

Links

Copyright-Bestimmungen: siehe Über diese Seite