Ü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 zajCsak 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):
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 írjukResult
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
Forrás: habr.com
