Hallo! Meine erste Artikelreihe wird sich mit den Methoden zur Kompression und Speicherung von Bildern/Ton befassen, wie JPEG (Bild) und WAVE (Audio), und sie wird auch praktische Beispiele von Programmen mit diesen Formaten (.jpg, .wav) enthalten. In diesem Teil werden wir uns speziell WAVE widmen.
Geschichte
WAVE (Waveform Audio File Format) ist ein Containerformat zur Speicherung von Audio-stream. Dieser Container wird in der Regel zur Speicherung von unkomprimiertem Ton in Puls-Code-Modulation verwendet. (Aus Wikipedia)
Es wurde 1991 zusammen mit RIFF von den Unternehmen Microsoft und IBM (Führende IT-Unternehmen zu dieser Zeit) erfunden und veröffentlicht.
Dateistruktur
Die Datei hat einen Header, die eigentlichen Daten, aber keinen Footer. Der Header hat insgesamt 44 Byte.
Im Header befinden sich die Einstellungen für die Anzahl der Bits pro Sample, die Abtastrate, die Tiefeninformationen und ähnliches, die für die Soundkarte erforderlich sind. (Alle numerischen Werte in der Tabelle müssen im Little-Endian-Format aufgezeichnet werden)
Blockname
Blockgröße (B)
Beschreibung/Zweck
Wert (manche sind festgelegt)
chunkId
4
Definition der Datei als Mediencontainer
0x52494646 im Big-Endian ("RIFF")
chunkSize
4
Die Größe der gesamten Datei ohne chunkId und chunkSize
FILE_SIZE — 8
format
4
Definition des Typs aus RIFF
0x57415645 im Big-Endian ("WAVE")
subchunk1Id
4
Um die Datei mehr Speicherplatz zu lassen für die Fortsetzung des Formats
0x666d7420 im Big-Endian ("fmt ")
subchunk1Size
4
Der verbleibende Header (in Bytes)
16 standardmäßig (im Fall ohne Kompression des Audiostreams)
audioFormat
2
Audioformat (abhängig von der Kompressionsmethode und Struktur der Audiodaten)
1 (für PCM, das wir betrachten)
numChannels
2
Anzahl der Kanäle
1/2, wir nehmen 1 Kanal (3/4/5/6/7… — spezifische Audiospur, z.B. 4 für Quad-Sound etc.)
sampleRate
4
Samplingfrequenz des Tons (in Hertz)
Je höher, desto hochwertiger wird der Ton sein, aber desto mehr Speicherplatz wird benötigt, um eine Audiospur der gleichen Länge zu erstellen; der empfohlene Wert liegt bei 48000 (die akzeptabelste Audioqualität)
byteRate
4
Anzahl der Bytes pro Sekunde
sampleRate numChannels bitsPerSample (siehe unten)
blockAlign
2
Anzahl der Bytes für 1 Sample
numChannels * bitsPerSample: 8
bitsPerSample
2
Anzahl der Bits pro Sample (Tiefe)
Eine beliebige Zahl, die ein Vielfaches von 8 ist. Je größer, desto besser und schwerer wird das Audio; ab 32 Bit gibt es keinen Unterschied für den Menschen
subchunk2Id
4
Kennzeichnung des Beginns der Daten (da je nach audioFormat andere Header-Elemente vorhanden sein können)
0x64617461 im Big-Endian („data“)
subchunk2Größe
4
Größe des Datenbereichs
Größe der Daten in int
data
byteRate * Audiodauer
Audiodaten
?
Beispiel mit WAVE
Die vorherige Tabelle lässt sich leicht in eine Struktur in C übersetzen, aber unsere Sprache heute ist Python. Das Einfachste, was man mit einer „Welle“ machen kann, ist ein Geräuschgenerator. Für diese Aufgabe benötigen wir keine hohe byteRate und keine Kompression.
Zuerst importieren wir die notwendigen Module:
# WAV.py
from struct import pack # перевод py-объектов в базовые типы из C
from os import urandom # функция для чтения /dev/urandom, для windows:
# from random import randint
# urandom = lambda sz: bytes([randint(0, 255) for _ in range(sz)]) # лямбда под windows, т.к. urandom'а в винде нет
from sys import argv, exit # аргументы к проге и выход
if len(argv) != 3: # +1 имя скрипта (-1, если будете замораживать)
print('Usage: python3 WAV.py [num of samples] [output]')
exit(1)Als nächstes müssen wir alle erforderlichen Variablen aus der Tabelle gemäß ihren Größen erstellen. Die nicht konstanten Werte hängen hier nur von numSamples (Anzahl der Samples) ab. Je mehr Samples, desto länger wird unser Geräusch.
numSamples = int(argv[1])
output_path = argv[2]
chunkId = b'RIFF'
Format = b'WAVE'
subchunk1ID = b'fmt '
subchunk1Size = b'x10x00x00x00' # 0d16
audioFormat = b'x01x00'
numChannels = b'x02x00' # 2 Kanäle sind ausreichend (Stereo)
sampleRate = pack('<L', 1000) # 1000 reicht, aber bei mehr hört man das Geräusch besser. Mit 1000 klingt es wie Wind
bitsPerSample = b'x20x00' # 0d32
byteRate = pack('<L', 1000 * 2 * 4) # sampleRate * numChannels * bitsPerSample / 8 (32-Bit-Sound)
blockAlign = b'x08x00' # numChannels * BPS / 8
subchunk2ID = b'data'
subchunk2Size = pack('<L', numSamples * 2 * 4) # * numChannels * BPS / 8
chunkSize = pack('<L', 36 + numSamples * 2 * 4) # 36 + subchunk2Size
data = urandom(1000 * 2 * 4 * numSamples) # das GeräuschNun müssen wir sie nur in der notwendigen Reihenfolge (wie in der Tabelle) aufzeichnen:
with open(output_path, 'wb') as fh:
fh.write(chunkId + chunkSize + Format + subchunk1ID +
subchunk1Size + audioFormat + numChannels +
sampleRate + byteRate + blockAlign + bitsPerSample +
subchunk2ID + subchunk2Size + data) # aufzeichnenUnd so, fertig. Um das Skript zu verwenden, müssen wir die erforderlichen Befehlszeilenargumente hinzufügen:
python3 WAV.py [Anzahl der Samples] [Ausgabe]
Anzahl der Samples — Anzahl der Samples
Ausgabe — Pfad zur Ausgabedatei
Hier ist ein Link zu einer Test-Audiodatei mit Geräusch, aber um Speicher zu sparen, habe ich BPS auf 1 b/s reduziert und die Anzahl der Kanäle auf 1 gesenkt (mit 32-Bit unkomprimiertem Stereo-Audiostream in 64 kbs ergaben sich 80 MB reines .wav, so nur 10):
Der gesamte Code (WAV.py) (Der Code hat viele Wiederholungen von Variablenwerten, dies ist nur ein Entwurf):
from struct import pack # Übersetzung von py-Objekten in grundlegende Typen aus C
from os import urandom # Funktion zum Lesen von /dev/urandom, für Windows:
# from random import randint
# urandom = lambda sz: bytes([randint(0, 255) for _ in range(sz)]) # Lambda für Windows, da es urandom unter Windows nicht gibt
from sys import argv, exit # Argumente für das Programm und Exit
if len(argv) != 3: # +1 Skriptname (-1, wenn Sie einfrieren wollen)
print('Nutzung: python3 WAV.py [Anzahl der Proben] [Ausgabe]')
exit(1)
numSamples = int(argv[1])
output_path = argv[2]
chunkId = b'RIFF'
Format = b'WAVE'
subchunk1ID = b'fmt '
subchunk1Size = b'x10x00x00x00' # 0d16
audioFormat = b'x01x00'
numChannels = b'x02x00' # 2 Kanäle sind ausreichend (Stereo)
sampleRate = pack('<L', 1000) # 1000 ist genug, kann aber auch mehr sein.
bitsPerSample = b'x20x00' # 0d32
byteRate = pack('<L', 1000 * 2 * 4) # sampleRate * numChannels * bitsPerSample / 8 (32 Bit Ton)
blockAlign = b'x08x00' # numChannels * BPS / 8
subchunk2ID = b'data'
subchunk2Size = pack('<L', numSamples * 2 * 4) # * numChannels * BPS / 8
chunkSize = pack('<L', 36 + numSamples * 2 * 4) # 36 + subchunk2Size
data = urandom(1000 * 2 * 4 * numSamples) # der eigentliche Lärm
with open(output_path, 'wb') as fh:
fh.write(chunkId + chunkSize + Format + subchunk1ID +
subchunk1Size + audioFormat + numChannels +
sampleRate + byteRate + blockAlign + bitsPerSample +
subchunk2ID + subchunk2Size + data) # Ergebnis in die Datei schreibenFazit
Jetzt wissen Sie ein wenig mehr über digitalen Klang und wie er gespeichert wird. In diesem Beitrag haben wir keine Kompression verwendet (audioFormat), aber um jedes der gängigen Formate zu betrachten, wären etwa 10 Artikel notwendig. Ich hoffe, Sie haben etwas Neues gelernt und das wird Ihnen bei künftigen Entwicklungen helfen.
Danke!
Quellen
Quelle: habr.com
