Busted and Blue
Als im letzten Jahr Gorillaz’ Humanz erschien und ich darauf hingewiesen wurde, dass die Domain busted.blue noch nicht vergeben war, wusste ich noch nicht was ich damit tun sollte. Seit ein paar Monaten liegt dort nun eine relativ Dynamische Seite, die die Lyrics des Liedes Busted and Blue beim laden verstreut1. Das allein wird schnell langweilig. Die nächste Idee war es, die Zeilen einzeln zu animieren, das Lied im Hintergrund zu spielen und die aktuelle Zeile hervorzuheben. Mit u.A. Musixmatch gibt es Sammlungen von Lyrics mit Timestamps, aber dort ist Busted and Blue noch nicht eingetragen.
Die Zeiten selbst aufzuschreiben ist zu fehleranfällig und langweilig. Ein ein wenig automatischerer Weg könnte sein, die Instrumente heruaszufiltern und die übrigen Ausschläge zu gruppieren. Nach zwei Wiederholungen von Audacitys Noise Reduction blieb eine sehr kaputte die Stimme mit wenigen Instrumenten übrig.
Als nächstes mussten die Audiodatei selbst gelesen werden. Dazu kann das bis hierher verwendete FLAC nach WAVE konvertiert werden, wofurch das lesen relativ einfach wird2.
WAVE
Das Format ist alt und gut dokumentiert. Es existieren Blogposts und gute Beschreibungen darüber. Vor allem The Sonic Spots Wave File Format war hilfreich und bietet eine genauere Erklärung der einzelnen Teile.
WAVEs liegen in RIFF-Dateien. RIFF-Dateien wiederum sind in mehrere (mindestens ein) Chunks aufgeteilt. Ein Chunk kann bspw. Metadaten über das Lied, das Lied selbst oder mehrere Teile dessen beinhalten. Chunks sind in einen acht Byte großen Header und im Chunk enthaltene Daten aufgeteilt. Die ersten vier Byte des Headers halten die Chunk ID (in ASCII), die letzten vier die Größe des Chunks exkl. dessen Header.
Das RIFF-Chunk, in dessen Payload alle weiteren Chunks liegen, hat eine ID “RIFF” und als Größe entsprechen die Dateigröße exkl. dem RIFF Header selbst. Darauf folgend, im Chunk selbst, steht die Type ID (hier fix “WAVE”) gefolgt von einem Format-Chunk und beliebig vielen weiteren.
Das folgende ist ein Beispiel für einen RIFF Header sowie die ersten vier darauffolgenden Bytes3.
// R, I, F, F, 2084, W, A, V, E,
0x52, 0x49, 0x46, 0x46, 0x24, 0x08, 0x00, 0x00, 0x57, 0x41, 0x56, 0x45,
ID: RIFF
Size: 2084
Type: WAVE
Nötig in WAVE sind lediglich ein RIFF
-, ein fmt
- und null, ein oder mehrere
data
-Chunks, die wie folgt aufgeteilt sind:
+----------------------+
| ID: RIFF |
| Size of File |
+----------------------+
| +------------------+ |
| | ID: fmt | |
| | Size of chunk | |
| +------------------+ |
| | ... | |
| +------------------+ |
| +------------------+ |
| | ID: data | |
| | Size of chunk | |
| +------------------+ |
| | 0x00, 0x00, 0x00 | |
| | 0x00, 0x24, 0x17 | |
| | 0x1e, 0xf3, ... | |
| +------------------+ |
| ... |
+----------------------+
Format-Chunk
Das Format Chunk hat eine ID fmt
(f
, m
, t
, Space) und ist i.d.R. 8+16
Byte groß. Es beinhaltet Metadata wie die Anzahl der Channels oder die größe der
einzelnen Samples. Diese sind in das Format (2 Byte), die Anzahl der Channels (1
= Mono, 2 = Stereo, 2 Byte), die Sample Rate (Abtastungen in Hz, 4 Byte), die
Byte Rate (4 Byte), sowie Block Align (Bytes pro Sample auf allen Channels) und
Bits Per Sample (je 2 Byte) aufgeteilt. Alle folgenden Chunks sind optional.
Als Beispiel die 8+16 Byte des Format-Chunks sowie dessen Übersetzung:
// F, M, T, ␣, 16, 1, 2,
0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00,
// 22050, 88200, 4, 16,
0x22, 0x56, 0x00, 0x00, 0x88, 0x58, 0x01, 0x00, 0x04, 0x00, 0x10, 0x00,
ID: fmt
Size: 16
AudioFormat: 1
Chans: 2
SampleRate: 22050
ByteRate: 88200
BlockAlign: 4
BitsPerSample: 16
Data-Chunks
Data-Chunks haben eine ID data
und size
Bytes an Samples.
// d, a, t, a, 28, 0, 0,
0x64, 0x61, 0x74, 0x61, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 5924, -3298, 4924, 5180, -1770, -1768,
0x24, 0x17, 0x1e, 0xf3, 0x3c, 0x13, 0x3c, 0x14, 0x16, 0xf9, 0x18, 0xf9,
// -6348, -23005, -3524, -3548, -12783, 3354,
0x34, 0xe7, 0x23, 0xa6, 0x3c, 0xf2, 0x24, 0xf2, 0x11, 0xce, 0x1a, 0x0d,
ID: data
Size: 28
Wie im Format-Chunk beschrieben, verwendet dieses Beispiel 16 Bits pro Sample auf zwei Channels.
Channels folgen aufeinander. Das erste Sample beider Channels ist 0, das zweite links ist 5924 und rechts -3298.
Weitere Chunks
Neben Format- und Data-Chunks existieren noch weitere, die u.a. im Sonic Spot beschrieben werden. Es ist möglich mehrere Data-Chunks zu verwenden, allerdings bestehen Lieder i.d.R. aus einem.
Plotten
Nachdem die einzelnen Samples nun mit bake/wave gelesen und geschrieben werden können, können sie auch geplottet werden. Das oben verwendete Beispiel Busted and Blue kann bspw. mit einer Version des Liedes, der die Instrumente größtenteils entfernt wurden verglichen werden:
Das Beispiel liegt in Originalgröße hier4.
Ähnlich mebious.co.uk und mebious.mobi aus Serial Experiments Lain. ↩︎
Ich habe mir lediglich WAV angeschaut. ↩︎
Dieses Beispiel stammt aus der Doku von soundfile++. Es ist außerdem im GoDoc des Projekts vorhanden. ↩︎
In der Vergrößerung ist erkennbar, dass die Samples nur als Pixel eingezeichnet werden, nicht als Graph. ↩︎