Metody pro kompresi/ukládání mediálních dat ve formátech WAVE a JPEG, část 1

Ahoj! Moje první série článků se zaměří na studium způsobů komprese obrazu/audia a jejich ukládání, jako jsou JPEG (obraz) a WAVE (zvuk), a budou obsahovat i příklady programů využívajících tyto formáty (.jpg, .wav) v praxi. V tomto díle se podíváme na WAVE.

Příběh

WAVE (Waveform Audio File Format) je kontejnerový formát souboru pro ukládání záznamu audio streamu. Tento kontejner se obvykle používá k ukládání nekomprimovaného audia s modulací pulzního kódu. (Převzato z Wikipedie)

Byl vynalezen a publikován v roce 1991 společně s RIFF společnostmi Microsoft a IBM (přední IT společnosti té doby).

Struktura souboru

Soubor má část záhlaví, samotná data, ale nemá zápatí. Hlavička váží celkem 44 bajtů.
Hlavička obsahuje nastavení počtu bitů ve vzorku, vzorkovací frekvence, hloubky zvuku atd. informace potřebné pro zvukovou kartu. (Všechny číselné hodnoty tabulky musí být zapsány v Little-Endian pořadí)

Název bloku
Velikost bloku (B)
Popis/účel
Hodnota (pro některé je pevná

chunkId
4
Definování souboru jako mediálního kontejneru
0x52494646 v Big-Endian (“RIFF”)

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

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

subchunk1Id
4
Aby soubor zabral více místa pokračováním ve formátu
0x666d7420 v Big-Endian („fmt“)

subchunk1Size
4
Zbývající záhlaví (v bajtech)
16 ve výchozím nastavení (pro případ bez komprese audio streamu)

audioformát
2
Formát zvuku (závisí na metodě komprese a struktuře zvukových dat)
1 (pro PCM, což je to, co zvažujeme)

numChannels
2
Počet kanálů
1/2, vezmeme 1 kanál (3/4/5/6/7... - konkrétní zvuková stopa, například 4 pro quad audio atd.)

vzorkovací frekvence
4
Vzorkovací frekvence zvuku (v Hertzech)
Čím vyšší, tím lepší bude zvuk, ale tím více paměti bude potřeba k vytvoření zvukové stopy stejné délky, doporučená hodnota je 48000 (nejpřijatelnější kvalita zvuku)

byteRate
4
Počet bajtů za sekundu
vzorkovací frekvence numChannels bitsPerSample (dále)

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

bitsPerSample
2
Počet bitů na 1 vzorek (hloubka)
Jakékoli číslo, které je násobkem 8. Čím vyšší číslo, tím lepší a těžší bude zvuk; od 32 bitů není pro člověka žádný rozdíl

subchunk2Id
4
Referenční značka dat (protože v závislosti na formátu zvuku mohou existovat další prvky záhlaví)
0x64617461 v Big-Endian("data")

subchunk2Size
4
Velikost datové oblasti
velikost dat v int

datum
byteRate * délka zvuku
Zvuková data
?

Příklad WAVE

Předchozí tabulku lze snadno přeložit do struktury v C, ale naším dnešním jazykem je Python. Nejjednodušší věc, kterou můžete udělat, je použít „vlnu“ - 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 musíme z tabulky vytvořit všechny potřebné proměnné podle jejich velikostí. Hodnoty proměnných v něm závisí pouze na numSamples (počet vzorků). Čím více jich bude, tím déle bude náš hluk pokračovat.

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)  # сам шум

Zbývá je zapsat v požadovaném pořadí (jako 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)  # записываем

A tak připraveni. Chcete-li použít skript, musíme přidat potřebné argumenty příkazového řádku:
python3 WAV.py [num of samples] [output]
počet vzorků - počet. Vzorky
output — cesta k výstupnímu souboru

Zde je odkaz na testovací zvukový soubor se šumem, ale kvůli úspoře paměti jsem snížil BPS na 1b/s a počet kanálů na 1 (s 32bitovým nekomprimovaným stereo audio streamem o rychlosti 64kbs se ukázalo, že 80 milionů čistého souboru .wav a pouze 10): https://instaud.io/3Dcy

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

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)  # записываем в файл результат

Celkový

Dozvěděli jste se tedy trochu více o digitálním zvuku a jeho ukládání. V tomto příspěvku jsme nepoužili kompresi (audioFormat), ale ke zvážení každého z populárních článků bude zapotřebí 10. Doufám, že jste se pro sebe naučili něco nového a pomůže vám to v budoucím vývoji.
Děkujeme!

zdroje

Struktura souborů WAV
WAV – Wikipedie

Zdroj: www.habr.com

Přidat komentář