Methoden zur Kompression/Speicherung von Mediendaten in den Formaten WAVE und JPEG, Teil 1

Hallo! Meine erste Serie von Artikeln wird sich mit Methoden zur Kompression und Speicherung von Bildern/Audios befassen, wie JPEG (Bild) und WAVE (Audio). Es werden auch Beispiele von Programmen vorgestellt, die diese Formate (.jpg, .wav) in der Praxis verwenden. In diesem Teil werden wir uns speziell mit WAVE beschäftigen.

Geschichte

WAVE (Waveform Audio File Format) ist ein Container-Dateiformat zur Speicherung von Audioflussaufnahmen. Dieser Container wird in der Regel zur Speicherung von unkomprimiertem Audio in Puls-Code-Modulation verwendet. (Aus Wikipedia entnommen)

Es wurde 1991 von den Firmen Microsoft und IBM zusammen mit RIFF entwickelt und veröffentlicht (Führende IT-Unternehmen dieser Zeit).

Die Struktur der Datei

Die Datei hat einen Header, die eigentlichen Daten, aber keinen Footer. Der Header hat insgesamt 44 Byte.
Im Header befinden sich Einstellungen zur Anzahl der Bits pro Sample, zur Abtastrate, zur Klangtiefe und weiteren Informationen, die für die Soundkarte erforderlich sind. (Alle numerischen Werte der Tabelle müssen im Little-Endian-Format notiert werden)

Blockname
Blockgröße (B)
Beschreibung/Zweck
Wert (bei einigen ist er festgelegt)

chunkId
4
Bestimmung der Datei als Mediencontainer
0x52494646 im Big-Endian-Format ("RIFF")

chunkSize
4
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
Damit die Datei mehr Platz benötigt, fortsetzung des Formats
0x666d7420 im Big-Endian („fmt “)

subchunk1Size
4
Verbleibender Header (in Bytes)
16 standardmäßig (für den Fall ohne Komprimierung des Audiostreams)

audioFormat
2
Audioformat (abhängig von der Komprimierungsmethode und der Struktur der Audiodaten)
1 (für PCM, das wir betrachten)

numChannels
2
Anzahl der Kanäle
1/2, wir wählen 1 Kanal (3/4/5/6/7… — spezifische Audiospur, z.B. 4 für Quadrophonie usw.)

sampleRate
4
Abtastfrequenz des Tons (in Hertz)
Je höher, desto besser die Klangqualität, aber es wird mehr Speicher benötigt, um einen Audiostream der gleichen Länge zu erstellen, der empfohlene Wert ist 48000 (das akzeptabelste Klangqualität)

byteRate
4
Anzahl der Bytes pro Sekunde
sampleRate numChannels bitsPerSample (folgend)

blockAlign
2
Anzahl der Bytes für 1 Sample
numChannels * bitsPerSample: 8

bitsPerSample
2
Anzahl der Bits pro Sample (Tiefe)
Jede Zahl, die durch 8 teilbar ist. Je höher, desto besser und schwerer das Audio, ab 32 Bit gibt es für den Menschen keinen Unterschied

subchunk2Id
4
Kennzeichnung des Beginns der Daten (da je nach audioFormat möglicherweise andere Header-Elemente vorhanden sind)
0x64617461 im Big-Endian („data“)

subchunk2Size
4
Größe des Datenbereichs
Größe der Daten in int

data
byteRate * Audio-Dauer
Audiodaten
?

Beispiel mit WAVE

Die vorherige Tabelle lässt sich einfach in eine Struktur in C übersetzen, aber unsere heutige Sprache ist Python. Das Einfachste, was wir mit «Welle» tun können, ist die Erzeugung von Rauschen. Für diese Aufgabe benötigen wir keine hohe byteRate und keine Kompression.
Zuerst importieren wir die benötigten 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 entsprechend ihren Größen erstellen. Die variablen Größen hängen hier nur von numSamples (Anzahl der Samples) ab. Je mehr Samples wir haben, desto länger wird unser Rauschen.

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 aus, aber wenn man mehr einstellt, wird das Rauschen besser hörbar. Bei 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 Rauschen selbst

Es bleibt nur noch, sie in der erforderlichen Reihenfolge aufzuschreiben (wie in der Tabelle):

with open(output_path, 'wb') as fh:
    fh.write(chunkId + chunkSize + Format + subchunk1ID +
            subchunk1Size + audioFormat + numChannels + 
            sampleRate + byteRate + blockAlign + bitsPerSample +
            subchunk2ID + subchunk2Size + data)  # schreiben

Und so, fertig. Um das Skript zu verwenden, müssen wir die erforderlichen Argumente für die Befehlszeile hinzufügen:
python3 WAV.py [num of samples] [output]
num of samples — Anzahl der Samples
output — Pfad zur Ausgabedatei

Hier ist der Link zu einer Test-Audiodatei mit Rauschen, aber um Speicherplatz zu sparen, habe ich die BPS auf 1b/s reduziert und die Anzahl der Kanäle auf 1 verringert (mit einem 32-Bit unkomprimierten Stereo-Audiostream in 64kbs ergab sich 80M reiner .wav-Datei, so nur 10): https://instaud.io/3Dcy

Der gesamte Code (WAV.py) (Der Code hat viele Wiederholungen der Variablenwerte, das ist nur ein Entwurf):

from struct import pack  # Py-Objekte in grundlegende Typen aus C umwandeln
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 in Windows nicht gibt
from sys import argv, exit  # Programmargumente und Ausgabe

if len(argv) != 3:  # +1 Name des Skripts (-1, wenn Sie es einfrieren)
    print('Verwendung: python3 WAV.py [Anzahl der Samples] [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 ausreichend, kann aber auch höher sein.
bitsPerSample = b'x20x00'  # 0d32
byteRate = pack('<L', 1000 * 2 * 4)  # sampleRate * numChannels * bitsPerSample \/ 8  (32-Bit Audio)
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)  # schreiben Sie das Ergebnis in die Datei

Zusammenfassung

Jetzt wissen Sie ein wenig mehr über digitalen Sound 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 zehn Artikel nötig. Ich hoffe, Sie haben etwas Neues gelernt, das Ihnen in zukünftigen Entwicklungen helfen wird.
Danke!

Quellen

Die Struktur einer WAV-Datei
WAV — Wikipedia

Quelle: habr.com

Zuverlässiges Webhosting mit DDoS-Schutz, VPS- und VDS-Server kaufen 🔥 Zuverlässiges Webhosting mit DDoS-Schutz, VPS- und VDS-Server kaufen | ProHoster