In den letzten beiden Teilen dieser Serie haben wir das Konzept des I(nterchange)-F(ile)-F(ormat) beschrieben. Heute erklären wir Ihnen den Aufbau des Formats für digitale Instrumente. Wie jedes Format im IFF hat auch diese Anwendung eine eigene Kennung (»8SVX«), durch die man die Klänge von z. B. Bildern unterscheidet. In die Kategorie »8SVX« fallen jedoch nicht nur Instrumente, auch Samples wie Sprache oder Musikstücke sind verwaltbar. Speziell für digitale Instrumente sahen die Entwickler des Standards die Angabe wichtiger Informationen über die Abspielweise vor, wie z. B. Lautstärken- oder Klangfarbenverläufe. Somit können Sie Klänge vielfältig beeinflussen.
Gebrauch von diesem Instrumentenstandard machen momentan nahezu alle Musikprogramme für den Amiga und den Macintosh, wodurch ein enormes Spektrum an Instrumenten und Samples zur Verfügung steht.
Um in einer IFF-Datei ein Instrument zu definieren, ist der FORM-Chunk mit der Kennung »8SVX« versehen. Wir bezeichnen ihn im weiteren Artikel mit »FORM 8SVX«. In diesem Chunk sind nun alle Informationen des Instrumentes bzw. Samples in kleineren Chunks (zu deutsch etwa »Strukturen«) dem Sinn nach zusammengefaßt. Eine typische Instrumentendatei leiten Sie beispielsweise folgendermaßen ein:
'FORM', Länge, '8SVX', ...
Die Informations-Chunks lassen sich in drei Bereiche gliedern: Die benötigten Parameter und Einstellungen des Instruments, die Daten des Klangs und zusätzliche Bemerkungen. Um den Aufbau und die Eigenschaften des Klangs festzuhalten, existiert der Parameter-Chunk »VHDR« (8-Bit-Voice-Header).
Die Komponenten dieser Struktur beschreiben die Länge der Sampledaten, deren Abspielfrequenz, die Anzahl der gespeicherten Oktaven, die Kompressionstechnik und die maximale Lautstärke. Die Struktur läßt sich in C formulieren als
typedef LONG Fixed; /* Ein Fließkommawert; die obersten 16 Bit */
/* ist der Vorkomma- und die niedrigsten 16 Bit */
/* der Nachkommawert (z.B. $100.$8000 = 256.5) */
typedef struct
{
ULONG oneShotHiSamples;
ULONG repeatHiSamples;
ULONG samplesPerHiCycles;
UWORD samplesPerSee;
UBYTE ctOctave;
UBYTE sCompression;
UBYTE Fixed_volume;
} Voice8Header;
Mit dem Parameter »ctOctave« geben Sie die Anzahl der gespeicherten Oktaven an. Diese sind im Datenteil so aneinandergereiht, daß die höchste Oktave am Anfang liegt. Wegen der Annahme, die nächst tiefere Oktave habe doppelt so viele Samples, reicht es vollkommen aus, die weiteren Instrumentenparameter direkt auf die höchste Oktave zu beziehen. Deswegen auch das »Hi« in den entsprechenden Parameternamen (siehe »oneShotHiSamples«).
Ein Klang gliedert sich in eine anfängliche unperiodische und eine folgende periodische Wellenform, die den eigentlichen Klang ausmacht. Deswegen stehen zwei Datenlängenangaben zur Verfügung: Mit »oneShotHiSamples« gibt man die Länge der geräuschhaften unperiodischen und mit »repeatHiSamples« die Länge der periodischen Wellenform an. Bei eigenen Aufnahmen sollten Sie diese beiden Teile so wählen, daß der One-Shot-Teil übergangslos in die periodische Phase übergeht.
Der Repeat-Teil sollte so beginnen und enden, daß beim Wiederholen dieses Teils keine Knackser hörbar sind. Es müssen aber nicht unbedingt beide Teile definiert sein; für einfache Samples reicht es aus, nur die One-Shot- oder Repeat-Datenlänge anzugeben. Verwenden Sie den Klang als Instrument, müssen Sie ebenfalls den Parameter »samplesPerHiCycles« bestimmen. Dieser gibt an, wie lange eine durchschnittliche Periode der Repeat-Wellenform ist. Soll aber das Sample nur zum reinen Abspielen dienen, so reicht die Angabe der Variablen »samplesPerSec«, welche die Abspielfrequenz vorgibt. Zur Verdeutlichung der bisherigen Angaben, sehen Sie in Bild 2 ein typisches digitales Instrument, und wie es durch die Voice8-Header-Parameter beschrieben wird. Mit dem Parameter »sCompression« bestimmen Sie die gewünschte Packtechnik. IFF unterstützt momentan den »Fibonacci-Delta-Pack«-Algorithmus, der die Länge der Sampledaten halbiert. Außerdem haben Sie die Gelegenheit, den Klang mit einer bestimmten Lautstärke zu versehen, die der Parameter »volume« festlegt. Dieser Wert besteht aus Vor- und Nachkommateil, z. B. 12.25. Der Integerwert (hier 12) steht in den oberen 16 Bit des Langwortes und der Nachkommawert (hier .25) ist in den unteren 16 Bit enthalten. Demnach wird der Wert 12.25 im Speicher als $000c-4000-Langwort abgelegt.
Soviel zu den Instrumentenparameter. Um einen natürlichen Klang zu erhalten, bietet das »8SVX«-Format die Definition einer Hüllkurve an. Hüllkurven beschreiben den Lautstärkeverlauf eines Tons. So untergliedert man diesen in der Musik in »Attack« (Anschwellen), »Decay« (leichtes Fallen), »Sustain« (Halten) und »Release« (Ausklingen). Einen typischen ADSR-Verlauf finden Sie in Bild 1. Haben Sie nur ein reines Sample zum Abspielen oder ein immer gleichbleibend lautes Instrument, so können Sie natürlich auf die Angabe der Hüllkurve verzichten. Für den Lautstärkeanstieg zu Beginn des Tons ist der »ATAK«-Chunk verantwortlich. Um einen Ton ausgleiten zu lassen, bedient man sich des»RLSE«-Chunks. Beide Chunks sind aus folgender C-Struktur aufgebaut:
typedef struct
{
UWORD duration; /* in millisec. */
Fixed dest; /* Lautstärkenfaktor */
} EGPoint;
Von dieser »EGPoint«-Struktur können beliebig viele in den Chunks hintereinanderliegen. Wollen Sie beispielsweise einen Ton in der Anklingphase von ganz leise bis ganz laut innerhalb einer gewissen Zeit anschwellen lassen, so benötigen Sie mindestens zwei Einträge im »ATAK«-Chunk. Die Abspielroutine müßte bei dieser Vorgabe zwischen den beiden Angaben interpolieren, um nicht sprunghaft von leise nach laut zu wechseln. Sie könnten in diesem Fall z. B. alle 1/10 Sekunde etwas anschwellen. Natürlich kann man auch sehr genaue Lautstärkenverläufe wie die Form einer Sinuskurve (siehe auch Bild 1) nachbilden. Wichtiger als die Einleitungsphase mit dem »ATAK«-Chunk ist für den Klang eines Instrumentes allerdings das Ausklingen mit dem »RLSE«-Chunk. Beide Lautstärkenverläufe, Attack und Release, enthalten absolute Haltezeitangaben in Millisekunden. Nun kann es vorkommen, daß die Tondauer zu kurz ist, um den Attack und das Release zu verarbeiten. In diesem Fall wird der Attack vorzeitig abgebrochen, um ein möglichst komplettes Abklingen durch das Release zu gewährleisten.
Doch nun zu den Klangdaten. Beachtenswert ist der Aufbau eines Samples. Der 8-Bit-Wert wird vorzeichenbehaftet behandelt und nimmt deswegen Werte zwischen -128 und 127 an. Alle Samples liegen im »BODY«-Chunk, und zwar mit den Daten der höchsten Oktave beginnend. Da der »VHDR«-Parameterchunk die Längenangabe nur für die höchste abgelegte Oktave des digitalen Instruments enthält, erlaubt die folgende Formel einen Zugriff auch auf die folgenden niedrigeren Oktaven:
OffsetToLower = 2 * NrLowOktave * (oneShotHiSamples + repeatHiSamples)
Um dem Instrument einen Namen zu geben, ist der »NAME«-Chunk vorgesehen. So kann er z. B. die Bemerkung »Tubular Bells« enthalten. Um ein Copyright auf das IFF-Instrument geltend zu machen, tragen Sie dieses in den »(c)«-Chunk ein. Ein typischer Vermerk ist z. B. »Copyright 1988, 89 ST-Magazin, M&T«. Für den Entwickler des Instruments steht der »AUTH«-Chunk zur Verfügung. Platz für weitere Bemerkungen zu dem Instrument bietet der »ANNO«-Chunk. Und so könnte eine IFF-Instrumentendatei aufgebaut sein:
FORM, 368, 8SVXI
IVHDRI, 20
24, 16, 8, 10000, 3, 0, 1.0
NAME, 11
bass guitar+0
(c), 20
1985 Electronic Arts
BODY, 280
1,2,3,4....
Um Ihnen die Einbindung der Instrumente in Ihre eigenen Programme zu erleichtern, haben wir im Listing alle notwendigen Routinen zusammengefaßt. Das Programm spielt IFF-Instrumente ab. Dabei können Sie ein Instrument nachladen oder das eingebaute Sinuskurven-Instrument verwenden. Durch die Tastatur bestimmen Sie die Tonhöhe. Dabei sind die oberen drei Tastenreihen < 1 > bis <'>, bis <+> und bis < Return > mit den zwölf Halbtönen der Oktaven -1,0 und 1 belegt. Der 440-Hz-Ton A0 entspricht der Taste < p >. Den Ton unterbrechen Sie mit der Leertaste. Mit < Escape > verlassen Sie das Programm.
Das Programm beginnt mit der Routine »main«. »select_instrument« (Zeile 260 ff) lädt die durch die Namenseingabe spezifizierte Instrumentendatei in den dafür vorgesehenen Puffer »iffptr«. Benutzen Sie statt einem nachgeladenen Instrument das im Programm enthaltene Demoinstrument, so kopiert das Programm dessen Daten in den Puffer. Die Puffergröße ist dabei die doppelte Instrumentenlänge. Dies ist notwendig, um ein komprimiertes Instrument zu entfalten (»DUncompack«, Zeile 290ff) und keinen neuen Speicher nachträglich zu belegen. Da das Programm drei Oktaven spielen kann, muß das Instrument in drei Oktaven vorliegen. Sollte das geladene Instrument über zuwenig oder zuviel Oktaven verfügen, berechnet die Routine »prepare8svx« (Zeile 198 ff) fehlende Oktaven und fügt diese ein, bzw. entfernt überflüssige Oktaven. Nun folgt das Unterprogramm »play_8svx« (Zeile 444 ff). Es initialisiert zuerst das System und bereitet den Timer-A-Interrupt vor. Trotzdem die Interrupt-Routine in C geschrieben ist, besteht der Interrupt-Ein- und Aussprung aus Maschinenbefehlen, die in C nicht formulierbar sind. »create_irq_handler« (Zeile 313 ff) trifft alle wichtigen Vorbereitungen zur Interruptverwaltung. Die Abspielroutine schaltet nun über die BIOS-Funktion »Xbtimer« den Timer-A ein und startet somit die IRQ-Routine (»irq_c_prg« in Zeile 344 ff).
In Zeile 471 wartet das Programm auf Tastatureingaben. Drückt der Anwender eine Taste, so berechnet das Programm daraus die Tonhöhe (Zeile 475 ff). Dabei wählt die Routine zuerst die richtige Oktave aus und setzt daraufhin den sogenannten Abtastzähler. Das Programm spielt das Instrument immer mit einer festen Frequenz ab (ca. 4 kHz) und kann somit die Tonhöhe nicht durch Frequenzänderung variieren. Deswegen benutzt es eine anderen Technik, um die verschiedenen Tonlagen zu spielen: Es berechnet, wie viele Samples pro Interruptaufruf übersprungen werden, bzw. wie viele Interruptaufrufe lang ein Sample zu halten ist. Dazu ein Beispiel: Wir spielen mit konstanten 4 kHz ein Instrument, das eigentlich mit 1 kHz abzutasten wäre. Die Abtastfrequenz von 1 kHz ist ebenfalls erreichbar, wenn man in den 4000 Interruptaufrufen pro Sekunde nur jedes vierte Mal ein neues Sample spielt, ansonsten das alte hält. Dies wird durch den Abtastzähler erreicht, der in diesem Fall den Wert 0.25 hat (4 x 0.25 = 1). Um mit Nachkommastellen in einfachen Langwortvariablen zu rechnen, sehen wir die unteren 10 Bit als Nachkomma- und die oberen 22 Bit als Vorkommastelle an. Das Interrupt-Programm arbeitet zunächst den OneShot-Teil des Samples ab. Daraufhin wiederholt es den Repeat-Teil bis zum Abbruch des Tons. Die Sample dienen dabei als Index auf eine Lautsprecherwertetabelle. (ba)
Literaturverweis:
Amiga ROM Kernel Reference Manual: Exec, Addison Wesley, ISBN 0-201-11099-7 DOWNLOAD 'iffplayer.zip' (8 KB)