Metode stiskanja/shranjevanja medijskih podatkov v formatih WAVE in JPEG, 1. del

Zdravo! Moja prva serija člankov se bo osredotočila na preučevanje metod stiskanja slike/zvoka in shranjevanja, kot sta JPEG (slika) in WAVE (zvok), vključevala pa bo tudi primere programov, ki uporabljajo ta formata (.jpg, .wav) v praksi. V tem delu si bomo ogledali WAVE.

Zgodba

WAVE (Waveform Audio File Format) je vsebniški format datoteke za shranjevanje posnetka zvočnega toka. Ta vsebnik se običajno uporablja za shranjevanje nestisnjenega zvoka, moduliranega s pulzno kodo. (Povzeto iz Wikipedije)

Izumili in izdali sta ga leta 1991 skupaj z RIFF Microsoft in IBM (vodilna IT podjetja tistega časa).

Struktura datoteke

Datoteka ima del glave, same podatke, ne pa tudi noge. Glava tehta skupno 44 bajtov.
Glava vsebuje nastavitve za število bitov v vzorcu, hitrost vzorčenja, globino zvoka itd. informacije, potrebne za zvočno kartico. (Vse vrednosti numerične tabele morajo biti zapisane v vrstnem redu Little-Endian)

Ime bloka
Velikost bloka (B)
Opis/Namen
Vrednost (za nekatere je fiksna

chunkId
4
Definiranje datoteke kot medijske vsebine
0x52494646 v Big-Endianu (»RIFF«)

chunkSize
4
Velikost celotne datoteke brez chunkId in chunkSize
FILE_SIZE - 8

format
4
Definicija tipa iz RIFF
0x57415645 v Big-Endianu (»WAVE«)

subchunk1Id
4
Tako da datoteka zavzame več prostora kot nadaljevanje formata
0x666d7420 v Big-Endianu (»fmt«)

subchunk1Size
4
Preostala glava (v bajtih)
16 privzeto (za primer brez stiskanja zvočnega toka)

audioFormat
2
Zvočni format (odvisno od metode stiskanja in strukture zvočnih podatkov)
1 (za PCM, kar razmišljamo)

numChannels
2
Število kanalov
1/2, vzeli bomo 1 kanal (3/4/5/6/7 ... - določen zvočni posnetek, na primer 4 za quad zvok itd.)

sampleRate
4
Hitrost vzorčenja zvoka (v Hertzih)
Višja kot je, boljši bo zvok, a več pomnilnika bo potrebno za ustvarjanje zvočnega posnetka enake dolžine, priporočena vrednost je 48000 (najbolj sprejemljiva kakovost zvoka)

byteRate
4
Število bajtov na sekundo
sampleRate numChannels bitsPerSample (nadalje)

blockAlign
2
Število bajtov za 1 vzorec
numChannels * bitsPerSample: 8

bitsPerSample
2
Število bitov na 1 vzorec (globina)
Vsako število, ki je večkratnik 8. Višje kot je število, boljši in težji bo zvok; od 32 bitov ni nobene razlike za ljudi

subchunk2Id
4
Podatkovna referenčna oznaka (ker lahko obstajajo drugi elementi glave, odvisno od audioFormat)
0x64617461 v Big-Endianu ("podatki")

subchunk2Size
4
Velikost podatkovnega območja
velikost podatkov v int

datum
byteRate * trajanje zvoka
Zvočni podatki
?

Primer WAVE

Prejšnjo tabelo je mogoče enostavno prevesti v strukturo v C, vendar je naš današnji jezik Python. Najlažje je, da uporabite "val" - generator hrupa. Za to nalogo ne potrebujemo visoke hitrosti bajtov in stiskanja.
Najprej uvozimo potrebne 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)

Nato moramo iz tabele ustvariti vse potrebne spremenljivke glede na njihove velikosti. Vrednosti spremenljivk v njem so odvisne samo od numSamples (število vzorcev). Več ko jih bo, dlje bo trajal naš hrup.

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

Vse kar ostane je, da jih zapišete v zahtevanem zaporedju (kot v tabeli):

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

In tako, pripravljeno. Za uporabo skripta moramo dodati potrebne argumente ukazne vrstice:
python3 WAV.py [num of samples] [output]
št. vzorcev - št. vzorcev
izhod — pot do izhodne datoteke

Tukaj je povezava do preskusne zvočne datoteke s šumom, vendar sem zaradi varčevanja s pomnilnikom znižal BPS na 1b/s in znižal število kanalov na 1 (z 32-bitnim nestisnjenim stereo zvočnim tokom pri 64 kbs se je izkazalo, da je 80M čiste datoteke .wav in samo 10): https://instaud.io/3Dcy

Celotna koda (WAV.py) (koda ima veliko podvojenih vrednosti spremenljivk, to je le skica):

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

Skupaj

Tako ste izvedeli nekaj več o digitalnem zvoku in njegovem shranjevanju. V tem prispevku nismo uporabili kompresije (audioFormat), ampak za obravnavo vsakega od priljubljenih bo potrebnih člankov 10. Upam, da ste se naučili nekaj novega zase in da vam bo to pomagalo pri prihodnjem razvoju.
Hvala!

viri

Struktura datoteke WAV
WAV - Wikipedia

Vir: www.habr.com

Dodaj komentar