Methoden zum Komprimieren/Speichern von Mediendaten in den Formaten WAVE und JPEG, Teil 1

Guten Tag! Meine erste Artikelserie konzentriert sich auf die Untersuchung von Bild-/Audiokomprimierungs- und Speichermethoden wie JPEG (Bild) und WAVE (Ton) und enthält auch Beispiele für Programme, die diese Formate (.jpg, .wav) in der Praxis verwenden. In diesem Teil werden wir uns WAVE ansehen.

Geschichte

WAVE (Waveform Audio File Format) ist ein Containerdateiformat zum Speichern einer Aufnahme eines Audiostreams. Dieser Container wird normalerweise zum Speichern unkomprimierter pulscodemodulierter Audiodaten verwendet. (Entnommen aus Wikipedia)

Es wurde 1991 zusammen mit RIFF von Microsoft und IBM (den damals führenden IT-Unternehmen) erfunden und veröffentlicht.

Dateistruktur

Die Datei hat einen Kopfteil, die Daten selbst, aber keinen Fußteil. Der Header wiegt insgesamt 44 Bytes.
Die Kopfzeile enthält Einstellungen für die Anzahl der Bits im Sample, die Samplerate, die Klangtiefe usw. Informationen, die für die Soundkarte benötigt werden. (Alle numerischen Tabellenwerte müssen in Little-Endian-Reihenfolge geschrieben werden)

Blockname
Blockgröße (B)
Beschreibung/Zweck
Wert (für einige ist er fest

chunkId
4
Definieren einer Datei als Mediencontainer
0x52494646 in Big-Endian („RIFF“)

stückgröße
4
Größe der gesamten Datei ohne chunkId und chunkSize
FILE_SIZE - 8

Format
4
Typdefinition von RIFF
0x57415645 in Big-Endian („WAVE“)

subchunk1Id
4
Damit die Datei als Fortsetzung des Formats mehr Platz einnimmt
0x666d7420 in Big-Endian („fmt“)

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

Audio Format
2
Audioformat (hängt von der Komprimierungsmethode und der Audiodatenstruktur ab)
1 (für PCM, was wir in Betracht ziehen)

AnzahlKanäle
2
Anzahl der Kanäle
1/2, wir nehmen 1 Kanal (3/4/5/6/7... – eine bestimmte Audiospur, zum Beispiel 4 für Quad-Sound usw.)

Beispielrate
4
Audio-Abtastrate (in Hertz)
Je höher, desto besser ist der Klang, aber desto mehr Speicher wird benötigt, um eine Audiospur gleicher Länge zu erstellen. Der empfohlene Wert ist 48000 (die akzeptabelste Klangqualität).

ByteRate
4
Anzahl der Bytes pro Sekunde
Beispielrate AnzahlKanäle bitsPerSample (weiter)

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

bitsPerSample
2
Anzahl der Bits pro 1 Sample (Tiefe)
Jede Zahl, die ein Vielfaches von 8 ist. Je höher die Zahl, desto besser und schwerer wird der Ton; ab 32 Bit gibt es für Menschen keinen Unterschied

subchunk2Id
4
Datenreferenzmarke (da es je nach audioFormat möglicherweise andere Header-Elemente gibt)
0x64617461 in Big-Endian („Daten“)

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

technische Daten
byteRate * Audiodauer
Audiodaten
?

WAVE-Beispiel

Die vorherige Tabelle lässt sich leicht in eine Struktur in C übersetzen, aber unsere heutige Sprache ist Python. Am einfachsten ist es, eine „Welle“ zu verwenden – einen Rauschgenerator. Für diese Aufgabe benötigen wir keine hohe Byterate und Komprimierung.
Importieren wir zunächst 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 notwendigen Variablen entsprechend ihrer Größe aus der Tabelle erstellen. Die darin enthaltenen Variablenwerte hängen nur von numSamples (Anzahl der Proben) ab. Je mehr es davon gibt, desto länger wird unser Lärm anhalten.

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-х каналов будет достаточно (стерео)
sampleRate = pack('<L', 1000)  # 1000 хватит, но если поставить больше, то шум будет слышен лучше. С 1000-ю он звучит, как ветер
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)  # сам шум

Jetzt müssen Sie sie nur noch in der erforderlichen Reihenfolge (wie in der Tabelle) aufschreiben:

with open(output_path, 'wb') as fh:
    fh.write(chunkId + chunkSize + Format + subchunk1ID +
            subchunk1Size + audioFormat + numChannels + 
            sampleRate + byteRate + blockAlign + bitsPerSample +
            subchunk2ID + subchunk2Size + data)  # записываем

Und so, fertig. Um das Skript verwenden zu können, müssen wir die erforderlichen Befehlszeilenargumente hinzufügen:
python3 WAV.py [num of samples] [output]
Anzahl der Proben – Anzahl. Proben
Ausgabe – Pfad zur Ausgabedatei

Hier ist ein Link zu einer Test-Audiodatei mit Rauschen, aber um Speicherplatz zu sparen, habe ich die BPS auf 1b/s und die Anzahl der Kanäle auf 1 gesenkt (bei einem unkomprimierten 32-Bit-Stereo-Audiostream mit 64 kbs stellte sich heraus, dass dies der Fall war). 80 MB reine WAV-Datei und nur 10): https://instaud.io/3Dcy

Der gesamte Code (WAV.py) (Der Code enthält viele doppelte Variablenwerte, dies ist nur eine Skizze):

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)

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-х каналов будет достаточно (стерео) 
sampleRate = pack('<L', 1000)  # 1000 хватит, но можно и больше.
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)  # сам шум

with open(output_path, 'wb') as fh:
    fh.write(chunkId + chunkSize + Format + subchunk1ID +
            subchunk1Size + audioFormat + numChannels + 
            sampleRate + byteRate + blockAlign + bitsPerSample +
            subchunk2ID + subchunk2Size + data)  # записываем в файл результат

Ergebnis

Sie haben also etwas mehr über digitalen Ton und dessen Speicherung erfahren. In diesem Beitrag haben wir keine Komprimierung (AudioFormat) verwendet, aber um jeden der beliebten zu berücksichtigen, sind 10 Artikel erforderlich. Ich hoffe, Sie haben etwas Neues für sich gelernt und dies wird Ihnen bei zukünftigen Entwicklungen helfen.
Vielen Dank!

Quellen

WAV-Dateistruktur
WAV – Wikipedia

Source: habr.com

Kommentar hinzufügen