A médiafájlok WAVE és JPEG formátumú tömörítési/tárolási módszerei, 1. rész

Üdvözlöm! Az első cikkem sorozat célja, hogy bemutassa a képek/zene, mint például a JPEG (kép) és a WAVE (hang) tömörítési és tárolási módszereit, valamint gyakorlati példákat a használatukra (.jpg, .wav). Ebben a részben a WAVE formátumra fogunk összpontosítani.

Történet

A WAVE (Waveform Audio File Format) egy fájlformátum, amely az audioáramlás rögzítésére szolgáló konténer. Ezt a konténert általában tömörítetlen hang tárolására használják impulzus-kód modulációval. (Forrás: Wikipédia)

1991-ben találták ki és adták ki a RIFF formátummal együtt a Microsoft és az IBM cégek. (A korabeli vezető IT cégek).

A fájl struktúrája

A fájl rendelkezik egy fejléc részsel, maga a tartalommal, de nincs lábléce. A fejléc teljesen 44 bájt.
A fejlécben találhatóak a mintában lévő bitek száma, a mintavételezési frekvencia, a hang mélysége és egyéb, a hangkártyához szükséges információk. (Minden numerikus értéknek little-endian sorrendben kell szerepelnie)

A blokk neve
A blokk mérete (B)
Leírás/Cél
Érték (néhány esetben fix)

chunkId
4
A fájl meghatározása mint médialáda
0x52494646 big-endian formátumban („RIFF”)

chunkSize
4
A fájl teljes mérete a chunkId és chunkSize nélkül
FILE_SIZE — 8

format
4
A RIFF típusának meghatározása
0x57415645 big-endian formátumban („WAVE”)

subchunk1Id
4
A fájl méretének növelése a format kiterjesztésén keresztül
0x666d7420 big-endian formátumban („fmt ”)

subchunk1Size
4
A megmaradt fejléc (byte-ban)
Alapértelmezett érték 16 (tömörítetlen audioáram esetén)

audioFormat
2
Az audio formátum (a tömörítési módszertől és az audioadatok struktúrájától függ)
1 (a PCM esetében, amelyet most vizsgálunk)

numChannels
2
Csatornák száma
1/2, mi 1 csatornát választunk (3/4/5/6/7… — specifikus audiósáv, például 4 a quadró hanghoz stb.)

sampleRate
4
A hang mintavételezési frekvenciája (Hertz-ben)
Minél nagyobb, annál jobb lesz a hangminőség, de annál több memória szükséges a ugyanannyi hosszú audiósáv létrehozásához, a javasolt érték 48000 (a legjobban elfogadható hangminőség)

byteRate
4
Byte-ok száma másodpercenként
sampleRate numChannels bitsPerSample (a következő)

blockAlign
2
Byte-ok száma 1 minta számára
numChannels * bitsPerSample: 8

bitsPerSample
2
Bitek száma 1 mintában (mélység)
Bármely szám, amely osztható 8-cal. Minél nagyobb, annál jobb és nehezebb lesz a hang, 32 bit-től kezdve nincs észlelhető különbség az ember számára

subchunk2Id
4
Az adatok kezdetének jelölése (mivel lehetnek más fejléc elemek az audioFormat függvényében)
0x64617461 Big-Endian formátumban („data“)

subchunk2Size
4
Adat terület mérete
data mérete int-ben

data
byteRate * a hang időtartama
Hangkódok
?

WAVE példa

A korábbi táblázat könnyen átírható C struktúrára, de a nyelvünk most Python. A legkönnyebb, amit a „hullám“ segítségével tehetünk, egy zajgenerátor. Ehhez a feladathoz nem lesz szükségünkre nagy byteRate-ra és tömörítésre.
Először importáljuk a szükséges modulokat:

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

Ezután létre kell hoznunk az összes szükséges változót a táblázat méreteiből. Az állandókként megjelenő értékek csak a numSamples-től (minták száma) függenek. Minél több lesz belőlük, annál tovább tart a zajunk.

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 csatorna elegendő (stereo)
sampleRate = pack('<L', 1000)  # 1000 elegendő, de ha több, akkor a zaj élesebben hallható. Ezerrel úgy hangzik, mint a szél
bitsPerSample = b'x20x00'  # 0d32
byteRate = pack('<L', 1000 * 2 * 4)  # sampleRate * numChannels * bitsPerSample / 8  (32 bit hang)
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)  # maga a zaj

Csak annyit kell tennünk, hogy a szükséges sorrendbe írjuk őket (ahogyan a táblázatban van):

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

És így készen vagyunk. A szkript használatához szükségünk van a megfelelő parancssori argumentumok megadására:
python3 WAV.py [samples száma] [kimenet]
samples száma — a minták száma
kimenet — a kimeneti fájl elérési útja

Itt egy link a zajmintát tartalmazó teszt audio fájlhoz, de a memóriatakarékosság érdekében a BPS-t 1b/s-ra csökkentettem, és a csatornák számát egyre csökkentem (a 32 bites tömörítetlen stereo audiófolyam 64kbs esetén 80M tiszta .wav fájl lett, így csak 10): https://instaud.io/3Dcy

Az egész kód (WAV.py) (A kód számos változó értékét megismétli, ez csak egy vázlat):

a struct import pack  # py-objectok C alap típusokká alakítása
from os import urandom  # funkció a /dev/urandom olvasásához, Windowsra:
# from random import randint
# urandom = lambda sz: bytes([randint(0, 255) for _ in range(sz)])  # lambda Windowsra, mivel urandom nincs Windows alatt
from sys import argv, exit  # program argumentumai és kilépés

if len(argv) != 3:  # +1 a script neve (-1, ha be akarja fagyasztani)
    print('Használat: python3 WAV.py [minták száma] [kimenet]')
    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 csatorna elegendő lesz (sztereó)
sampleRate = pack('<L', 1000)  # 1000 elegendő, de lehet több is.
bitsPerSample = b'x20x00'  # 0d32
byteRate = pack('<L', 1000 * 2 * 4)  # sampleRate * numChannels * bitsPerSample / 8  (32 bites hang)
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)  # maga a zaj

with open(output_path, 'wb') as fh:
    fh.write(chunkId + chunkSize + Format + subchunk1ID +
            subchunk1Size + audioFormat + numChannels + 
            sampleRate + byteRate + blockAlign + bitsPerSample +
            subchunk2ID + subchunk2Size + data)  # az eredményt a fájlba írjuk

Result

Most egy kicsit többet tudtál meg a digitális hangról és arról, hogyan tárolják azt. Ebben a bejegyzésben nem használtunk tömörítést (audioFormat), de mindegyik népszerű megvitatásához körülbelül 10 cikkre van szükség. Remélem új dolgokat tanultál, és ez segít a jövőbeli fejlesztéseidben.
Köszönöm!

Források

A WAV fájl felépítése
WAV — Wikipédia

Forrás: habr.com

Megbízható hosting vásárlása weboldalak számára, DDoS védelemmel, VPS VDS szerverek. 🔥 Megbízható webtárhely vásárlása DDoS védelemmel, VPS VDS szerverek | ProHoster