Meediaandmete kokkusurutud/säilitamise meetodid formaatides WAVE ja JPEG, osa 1

Tere! Minu esimene artikkelide seeria keskendub pildifailide ja helifailide kompressioonimeetodite uurimisele, sealhulgas JPEG (pildid) ja WAVE (heli) formaadid. Käesolevas osas vaatleme WAVE formaati.

Ajalugu

WAVE (Waveform Audio File Format) on konteinerifaili formaat audio voogude salvestamiseks. See konteiner kasutatakse tavaliselt kompressimata heli salvestamiseks impulss-koodimodulatsiooniga. (Allikas: Vikipeedia)

See formaat loodi ja avaldati 1991. aastal koos RIFFiga Microsofti ja IBM-i poolt (tollaste juhtivate IT ettevõtete poolt).

Faili struktuur

Failil on päis ja andmed, kuid ei ole jaluseid. Päis kaalub kokku 44 baiti.
Päises asuvad seaded bitide arvu, näidustussageduse, heli sügavuse jne kohta, vajalikud heli kaartide jaoks. (Kõik numbrilised väärtused tabelis peavad olema kirjutatud Little-Endian järjekorras)

Ploki nimi
Ploki suurus (B)
Kirjeldus/Eesmärk
Väärtus (mõnedes on see fikseeritud)

chunkId
4
Faili määramine meedia konteineriks
0x52494646 Big-Endiani vormingus («RIFF»)

chunkSize
4
Kogu faili suurus ilma chunkId ja chunkSize
FILE_SIZE — 8

format
4
Tüübi määramine RIFFist
0x57415645 Big-Endiani vormingus («WAVE»)

subchunk1Id
4
Et failil oleks rohkem ruumi, järgmine formaadi osa
0x666d7420 Big-Endiani vormingus («fmt «)

subchunk1Size
4
Ülejäänud päis (baitides)
16 vaikimisi (kompressimata audio voogude jaoks)

audioFormat
2
Heli formaat (sõltub kompressioonimeetodist ja audio andmete struktuurist)
1 (PCM jaoks, mida me arutame)

numChannels
2
Kanalite arv
1/2, me võtame 1 kanali (3/4/5/6/7… — spetsiifiline audio rada, näiteks 4 kvadro heli jne)

sampleRate
4
Heli näidustussagedus (Hertsides)
Mida suurem on see, seda parem on heli kvaliteet, kuid samas on selle jaoks vaja rohkem mälu sama pikkuse audio raja loomiseks, soovitatav väärtus on 48000 (parim heli kvaliteet)

byteRate
4
Baitide arv ühe sekundi jooksul
sampleRate numChannels bitsPerSample (edasi)

blockAlign
2
Baitide arv ühe näidise jaoks
numChannels * bitsPerSample: 8

bitsPerSample
2
Bitide arv ühe näidise jaoks (sügavus)
Igasugune number, mis on jagatav 8-ga. Mida rohkem, seda parem ja raskem on heli, alates 32 bitist ei ole inimesel vahet.

subchunk2Id
4
Andmete alguspunkti märge (kuna audioFormaadi tõttu võivad olla teised päise elemendid)
0x64617461 Big-Endiani vormingus («data»)

subchunk2Size
4
Andmeala suurus
data suurus int’ina

data
byteRate * audio kestus
Audio andmed
?

Näide WAVE'ist

Eelmise tabeli saab lihtsalt C-struktuuri tõlkida, kuid meie keel täna on Python. Lihtsaim, mis meil on, kasutades «WAVE'i» — müra generaator. Selle ülesande jaoks ei ole meil vaja kõrget byteRate'i ja kompressiooni.
Alustamiseks impordime vajalikud moodulid:

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

Seejärel peame looma kõik vajalikud muutujad tabelis nende suuruste põhjal. Muutuvate väärtuste suurus sõltub ainult numSamples'ist (näidiste arv). Mida rohkem neid on, seda kauem müra kestab.

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 kanali jaoks piisab (stereo)
sampleRate = pack('<L', 1000)  # 1000 piisab, kuid kui panna rohkem, siis müra kuuldub paremini. 1000-ga kõlab nagu tuul
bitsPerSample = b'x20x00'  # 0d32
byteRate = pack('<L', 1000 * 2 * 4)  # sampleRate * numChannels * bitsPerSample / 8  (32-bitis heli)
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)  # see on müra

Jäänud on vaid need vajalikus järjekorras kirjutada (nagu tabelis):

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

Nii et, see on valmis. Skripti kasutamiseks peame lisama vajalikud käsurea argumendid:
python3 WAV.py [näidiste arv] [väljund]
näidiste arv — näidiste arv
väljund — väljundfaili tee

Siin on link testfailile, kuid mälu säästmiseks vähendasin BPS-i 1b/s ja kanalite arvu 1-ni (32-bitisest kompressimata stereo helifailist 64kbs sai 80M puhta .wav failiga, kuid nüüd on see ainult 10): https://instaud.io/3Dcy

Kogu kood (WAV.py) (Kood sisaldab paljusid muutuja väärtuste dubleeringuid, see on vaid mustand):

from struct import pack  # py-objektide teisendamine C põhitüüpideks
from os import urandom  # funktsioon /dev/urandom'i lugemiseks, Windowsi jaoks:
# from random import randint
# urandom = lambda sz: bytes([randint(0, 255) for _ in range(sz)])  # Windowsi jaoks, kuna seal pole urandom'i
from sys import argv, exit  # argumendid programmile ja väljumine

if len(argv) != 3:  # +1 skripti nimi (-1, kui plaanite külmutada)
    print('Kasutus: python3 WAV.py [näidiste arv] [väljund]')
    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 kanalit on piisavalt (stereo)
sampleRate = pack('<L', 1000)  # 1000 on piisav, kuid võib ka rohkem olla.
bitsPerSample = b'x20x00'  # 0d32
byteRate = pack('<L', 1000 * 2 * 4)  # sampleRate * numChannels * bitsPerSample / 8  (32 bitine heli)
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)  # ise heli

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

Kokkuvõte

Nüüd teate veidi rohkem digitaalsest helist ja selle säilitamisest. Selles postituses ei kasutanud me tihendamist (audioFormat), kuid iga populaarse vormingu arutamiseks on vajalik umbes 10 artiklit. Loodan, et õppisite midagi uut ja see aitab teid tulevaste arenduste juures.
Aitäh!

Allikad

WAV faili struktuur
WAV — Vikipeedia

Allikas: habr.com

Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid 🔥 Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid | ProHoster