Metody komprese/uložení mediálních dat ve formátech WAVE a JPEG, část 1

Ahoj! Moje první série článků bude zaměřena na studium metod komprese a ukládání obrazů/hluků, jako jsou JPEG (obrázky) a WAVE (zvuk), také budou obsahovat příklady programů používajících tyto formáty (.jpg, .wav) v praxi. V této části se zaměříme konkrétně na WAVE.

Historie

WAVE (Waveform Audio File Format) je formát kontejnerového souboru pro ukládání záznamu audio streamu. Tento kontejner se obvykle používá pro uchovávání nekomprimovaného zvuku v impulzní kódové modulaci. (Čerpáno z Wikipedie)

Byl vynalezen a publikován v roce 1991 spolu s RIFF společnostmi Microsoft a IBM (Vedení IT firmy té doby).

Struktura souboru

Soubor má hlavičkovou část, vlastní data, ale neobsahuje patičku. Hlavička váží celkem 44 bajtů.
V hlavičce se nacházejí nastavení počtu bitů na vzorek, vzorkovací frekvence, hloubka zvuku a další informace potřebné pro zvukovou kartu. (Všechny číselné hodnoty v tabulce musí být zapsány v pořadí Little-Endian)

Název bloku
Velikost bloku (B)
Popis/Purpose
Hodnota (u některých je fixační)

chunkId
4
Definice souboru jako média kontejneru
0x52494646 v Big-Endian („RIFF“)

chunkSize
4
Velikost celého souboru bez chunkId a chunkSize
FILE_SIZE — 8

format
4
Definice typu z RIFF
0x57415645 v Big-Endian („WAVE“)

subchunk1Id
4
Pro větší prostor souboru pokračování format’u
0x666d7420 v Big-Endian („fmt “)

subchunk1Size
4
Zbytek hlavičky (v bajtech)
16 ve výchozím stavu (pro případ bez komprese audio streamu)

audioFormat
2
Audio formát (závisí na metodě komprese a struktuře audio dat)
1 (pro PCM, který zkoumáme)

numChannels
2
Počet kanálů
1/2, vezmeme 1 kanál (3/4/5/6/7… — specifická audio stopa, například 4 pro čtyřkanálový zvuk atd.)

sampleRate
4
Frekvence vzorkování zvuku (v Hertzích)
Čím vyšší, tím kvalitnější bude zvuk, ale tím více paměti bude potřeba pro vytvoření audio stopy stejné délky, doporučovaná hodnota — 48000 (nejpřijatelnější kvalita zvuku)

byteRate
4
Počet bajtů za 1 sekundu
sampleRate numChannels bitsPerSample (další)

blockAlign
2
Počet bajtů pro 1 vzorek
numChannels * bitsPerSample: 8

bitsPerSample
2
Počet bitů za 1 vzorek (hloubka)
Jakékoli číslo dělitelné 8. Čím více, tím lépe a těžší audio, od 32 bitů není rozdíl pro člověka

subchunk2Id
4
Označení začátku dat (protože mohou existovat další prvky hlavičky v závislosti na audioFormat)
0x64617461 v Big-Endian („data“)

subchunk2Size
4
Velikost oblasti dat
velikost dat v int’u

data
byteRate * délka audia
Audio data
?

Příklad WAVE

Předchozí tabulku lze snadno převést do struktury v C, ale náš jazyk pro dnešek je Python. Nejjednodušší, co můžeme udělat, pomocí „vlny“ — generátor šumu. Pro tento úkol nepotřebujeme vysoký byteRate a kompresi.
Nejprve naimportujeme potřebné moduly:

# 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)

Dále je nutné vytvořit všechny potřebné proměnné z tabulky podle jejich velikostí. Proměnné v ní závisí pouze na numSamples (počet vzorků). Čím více jich bude, tím déle bude náš šum trvat.

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ály budou dostatečné (stereo)
sampleRate = pack('<L', 1000)  # 1000 stačí, ale pokud dáte víc, šum bude slyšitelnější. Při 1000 zní jako vítr
bitsPerSample = b'x20x00'  # 0d32
byteRate = pack('<L', 1000 * 2 * 4)  # sampleRate * numChannels * bitsPerSample / 8  (32 bit zvuk)
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)  # samotný šum

Zbývá už jen zapisovat je v potřebném pořadí (jak v tabulce):

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

A máme hotovo. Pro použití skriptu musíme přidat potřebné argumenty příkazového řádku:
python3 WAV.py [počet vzorků] [výstup]
počet vzorků — kol. vzorků
výstup — cesta k výstupnímu souboru

Zde je odkaz na testovací audio soubor se šumem, ale pro úsporu místa jsem snížil BPS na 1b/s a počet kanálů na 1 (s 32bitovým nekomprimovaným stereo audio streamem na 64kbs bylo 80M čistého .wav souboru, a teď jen 10): https://instaud.io/3Dcy

Celý kód (WAV.py) (Kód má mnoho duplicitních hodnot proměnných, to je jen nástin):

z struct import pack  # převod py-objektů na základní typy z C
z os import urandom  # funkce pro čtení /dev/urandom, pro windows:
# z random import randint
# urandom = lambda sz: bytes([randint(0, 255) for _ in range(sz)])  # lambda pro windows, protože urandom v systému Windows není
z sys import argv, exit  # argumenty pro program a výstup

if len(argv) != 3:  # +1 název skriptu (-1, pokud budete mrazit)
    print('Použití: python3 WAV.py [počet vzorků] [výstup]')
    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ály budou stačit (stereofonní) 
sampleRate = pack('<L', 1000)  # 1000 bude stačit, ale může být i více.
bitsPerSample = b'x20x00'  # 0d32
byteRate = pack('<L', 1000 * 2 * 4)  # sampleRate * numChannels * bitsPerSample / 8  (32 bit zvuk)
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)  # šum

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

Výsledek

Teď jste se dozvěděli něco víc o digitálním zvuku a o tom, jak je uchováván. V tomto příspěvku jsme nepoužili kompresi (audioFormat), ale pro podrobné prozkoumání každého z populárních by bylo zapotřebí 10 článků. Doufám, že jste se naučili něco nového a pomůže vám to v budoucích projektech.
Děkuji!

Zdroje

Struktura WAV souboru
WAV – Wikipedia

Zdroj: habr.com

Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster