Um völlig formatunabhängig zu arbeiten, sollte an keiner Stelle des Programms der Name einer konkreten Klasse erscheinen. Wer die Audiodateien über eine Schnittstelle von außen her erhält, sollte als Parametertyp AudioData nennen:
Zur Zeit existieren die konkreten Klassen AudioFile und WaveFile. Die erste ist für SUN-au Dateien vorgesehen, die andere ermoeglicht das Arbeiten mit WAV-Dateien.
Offset | Bytes | Bemerkungen |
00H | 4 | Signatur '.snd' |
04H | 4 | Data Location |
08H | 4 | Data Size |
0CH | 4 | Data Format |
10H | 4 | Sampling Rate (Hz) |
14H | 4 | Channel Count |
18H | n | Char Info (ASCII) |
Inhalt von Data Format | Bedeutung | wird unterstuetzt |
1 | 8 Bit mu-law | ja |
2 | 8 Bit linear | ja |
3 | 16 Bit linear | ja |
4 | 24 Bit linear | nein |
5 | 32 Bit linear | nein |
6 | Floating point | nein |
7 | Double precision | nein |
8 | Formated sampled data | nein |
... | ... | ... |
Um mu-law Werte lesen/schreiben zu koennen, verfuegt AudioFile ueber Umrechnungstabellen und Umrechnungsfunktionen, die aus mu-law Samples 8 Bit grosse lineare Werte erzeugen und umgekehrt.
8 Bit grosse Abtastwerte werden beim Kopieren in den AudioCache (intern 16 Bit linear) nicht auf 16 Bit expandiert und beim zurueckschreiben in eine Datei nicht wieder komprimiert. Formate mit Abtastwerten ueber 16 Bit werden zur Zeit nicht unterstuetzt. AudioFile akzeptiert Dateien ohne Header. In solchen Faellen wird 8 Bit mu-law angenommen.
RIFF-Chunk |
Format-Chunk |
Data-Chunks... |
Der RIFF-Chunk und der Format-Chunk bilden den WAVE-Header. Der RIFF-Chunk
hat die Struktur:
Offset | Bytes | Bemerkungen |
00H | 4 | Signatur 'RIFF' |
04H | 4 | Chunk-Laenge |
08H | 4 | Signatur 'WAVE' |
Der Format-Chunk besitzt folgenden Aufbau:
Offset | Bytes | Bemerkungen |
00H | 4 | Signatur 'FMT ' |
04H | 4 | Chunk-Laenge |
08H | 2 | Format Typ:
0 = Mono 1 = Stereo |
0AH | 2 | Kanalzahl |
0CH | 4 | Sample Rate [Hz] |
10H | 4 | Bytes pro Sekunde |
14H | 2 | Bytes pro Sample
1 = 8 Bit Mono 2 = 8 Bit Stereo oder 16 Bit Mono 4 = 16 Bit Stereo |
16H | 2 | Bits pro Sample |
Aus 'Format Typ' und 'Bytes pro Sample' wird die in aulib++ verwendete
Codierung von Datenformat errechnet:
Format Typ | Bytes pro Sample | Format Code |
0 (Mono) | 1 | 8 Bit linear (lin8) |
0 | 2 | 16 Bit linear (lin16) |
1 (Stereo) | 2 | 8 Bit linear |
1 | 4 | 16 Bit linear |
Es sind folgende Abtastfrequenzen moeglich: 11025 Hz (11 2B 00 00H), 22050 Hz (22 56 00 00H), 44100 Hz (44 AC 00 00H). 'Bytes pro Sekunde' ist der Wert, der sich aus der Kanalzahl, Abtastrate und der Anzahl von Bytes pro Sample ergibt. 'Bits pro Sample' kann 8 im Falle von 1 Byte grossen Abtastwerten bzw. 12 oder 16 bei 2 Byte grosse Abtastwerten betragen.
An den WAVE-Header schliessen sich einer oder mehrere Data-Chunks an.
Jeder hat die Struktur:
Offset | Bytes | Bemerkung |
00H | 4 | Signatur 'data' |
04H | 4 | Chunk-Laenge |
08H | n | Daten |
Die Daten sind als eine Sequenz von Werten des im Header spezifizierten Formats abgelegt. Bei Stereo-Aufnahmen werden die Werte fuer den linken und rechten Kanal wechselweise gespeichert.
Es ist zu beachten, dass die Daten wortweise organisiert sind. Besitzt ein Chunk (insb. ein Data-Chunk) eine ungerade Anzahl von Bytes, so wird er mit einem Nullbyte aufgefuellt. Die Laengenangabe umfasst ihn allerdings nicht!
WaveFile kann nur auf den ersten Chunk einer WAVE-Datei zugreifen. In Normallfall duerfte es ausreichen, denn die meisten Dateien weisen nur einen solchen auf.
Eine Implementierung von AudioData mit freiem Zugriff auf einzelne Abtastwerte waere ineffizient. Ausserdem arbeiten die meisten Analysewerkzeuge nicht mit einzelnen Werten sondern mit ganzen Signalausschnitten. Jedes AudioData-Objekt verfuegt deshalb ueber einen sog. Audiocache.
Die Klasse AudioCache stellt einen sequentiellen Container dar, in dem eine beliebige Anzahl von Abtastwerten gespeichert werden kann. Das Datenformat der gespeicherten Werte ist 16 Bit linear. Es sind mono-Daten. Die Abtastfrequenz wird bei der erzeugung des AudioCaches festgelegt und kann danach nicht mehr veraendert werden. Es ist insbesondere nicht moeglich den Zuweisungsoperator (operator=) fuer zwei AudioCache-Objekte mit unterschiedlichen Abtastraten aufzurufen, da eine Umrechnungsfunktion der Art: "Daten bei Abtastfrequenz 1 -> dieselben Daten bei Abtastfrequenz 2" fehlt.
Die Kapazitaet des Caches kann ebelfalls bei der Erzeugung angegeben werden, muss aber nicht. Vereingestellt ist die groesse 0. Es ist moeglich die Groesse des Containers nachtraeglich zu veraendern. Die Veraenderungen sind destruktiv, d.h. der Inhalt des Containers geht dabei verloren.
Der Zugriff auf den Inhalt des Containers erfolgt mittels operator[] oder mit Hilfe der Elementfunktion at(size_t). Im ersten Fall wird der Index nicht ueberprueft, at() wirft bei falscher Indizierung eine ausnahme aus.
Die meisten Analyse- und Bearbeitungswerkzeuge arbeiten mit Daten, die in einem AudioCache-Objekt gespeichert sind. AudioData verwendet AudioCache als Puffer, um den Zugriff auf den Inhalt von Audiodateien zu beschleunigen. Mittels der Methode AudioData::load(int) koennen Samples aus der Audiodatei in den Audiocache geladen werden. Die Methode AudioData::seek(int, AudioData::SeekMode) dient zum Positionieren des Lesezeigers in der Audiodatei.
Die Schnittstelle von AudioCache erinnert an die Containerklassen der
STL, insb. die Klasse vector. Es fehlt allerdings ein Iterator. Es ist
deshalb nicht moeglich, die Algorithmen aus der Standardbibliothek zu benutzen.
Die Benutzung von ComplexFFT ist einfach. Bei der Erzeugung eines Werkzeugs werden die gewuenschte Fenstergroesse sowie die Fensterfunktion angegeben. Die Funktion fft(ComplexWave&) transformiert das Signal aus dem Zeit- in das Frequenzbereich, invfft(ComplexWave&) bewirkt das Gegenteil. Das Signal wird jeweils als ein Vektor von komplexen Zahlen uebergeben. Die Groesse des Vektors muss mit der voreingestellten Fenstergroesse exakt uebereinstimmen. Die Transformation ist destruktiv, d.h. die Daten gehen verloren und nach der Operation befindet sich in dem Vektor das Ergebnis.
Die Fenstergroesse und die Fensterfunktion koennen nachtraeglich veraendert werden. Obwohl die FFT ausschliesslich Fenstergroessen zulaesst, die 2-Potenzen sind, kann ein beliebiger Wert angegeben werden. ComplexFFT passt den Wert automatisch an:
benutzter Wert = Max { i | i <= angegebener Wert, i = 2^k }.
Folgende Fensterfunktionen stehen zur Verfuegung: Hanning, Hamming und Rechteck (= keine).
LinearFFT ist eine Template-Klasse. Der Template-Parameter bestimmt, welchen Typ die Werte von FFTData haben. So ist es moeglich, ja nach Bedarf z.B. int oder double zu verwenden.
Wie die Umrechnung von komplexen zu reellen Werten erfolgt, bestimmt
die Konversionsmethode. Sie kann bei der Erzeugung eines Werkzeugs angegeben
werden. Es stehen folgende Umrechnungsarten zur Verfuegung: AMP, DB, SQR,
PHASE und ADD.