Meediumiandmete tihendamise/salvestamise meetodid WAVE- ja JPEG-vormingus, 1. osa

Tere! Minu esimene artiklite seeria keskendub pildi/heli tihendamise ja salvestamise meetodite, nagu JPEG (pilt) ja WAVE (heli) uurimisele ning sisaldab ka näiteid programmidest, mis neid vorminguid (.jpg, .wav) praktikas kasutavad. Selles osas vaatleme lainet.

Lugu

WAVE (Waveform Audio File Format) on konteineri failivorming helivoo salvestise salvestamiseks. Seda konteinerit kasutatakse tavaliselt tihendamata impulsskoodiga moduleeritud heli salvestamiseks. (Võetud Wikipediast)

Selle leiutasid ja avaldasid 1991. aastal koos RIFF-iga Microsoft ja IBM (selle aja juhtivad IT-ettevõtted).

Faili struktuur

Failil on päise osa, andmed ise, aga jalust pole. Päis kaalub kokku 44 baiti.
Päis sisaldab sätteid valimi bittide arvu, diskreetimissageduse, heli sügavuse jne jaoks. helikaardi jaoks vajalik teave. (Kõik numbrilised tabeli väärtused tuleb kirjutada Little-Endia järjekorras)

Blokeeri nimi
Ploki suurus (B)
Kirjeldus/eesmärk
Väärtus (mõnede jaoks on see fikseeritud

chunkId
4
Faili määratlemine meediumikonteinerina
0x52494646 Big-endiani keeles (RIFF)

tüki suurus
4
Kogu faili suurus ilma chunkId ja chunkSizeta
FILE_SIZE – 8

formaat
4
Tüübi määratlus RIFF-ist
0x57415645 big-endiani keeles (WAVE)

alamtükk1Id
4
Nii et fail võtab vormingu jätkuna rohkem ruumi
0x666d7420 Big-endiani keeles (“fmt”)

alamtükk1Suurus
4
Ülejäänud päis (baitides)
Vaikimisi 16 (ilma helivoo tihendamiseta juhul)

helivorming
2
Helivorming (sõltub tihendusmeetodist ja heliandmete struktuurist)
1 (PCM-i jaoks, mida me kaalume)

numChannels
2
Kanalite arv
1/2, võtame 1 kanali (3/4/5/6/7... - konkreetne heliriba, näiteks 4 nelja heli jaoks jne)

proovimäär
4
Heli diskreetimissagedus (hertsides)
Mida kõrgem, seda parem on heli, kuid mida rohkem mälu on vaja sama pikkusega heliraja loomiseks, on soovitatav väärtus 48000 (kõige vastuvõetavam helikvaliteet)

byteRate
4
Baitide arv sekundis
proovimäär numChannels bitsPerSample (edaspidi)

blockAlign
2
1 proovi baitide arv
numChannels * bitsPerSample: 8

bitsPerSample
2
Bittide arv 1 proovi kohta (sügavus)
Mis tahes arv, mis on 8-kordne. Mida suurem on number, seda parem ja raskem on heli; alates 32 bitist pole inimeste jaoks vahet

alamtükk2Id
4
Andmete viitemärk (kuna helivormingust olenevalt võib päiseelemente olla ka teisi)
0x64617461 Big-endiani keeles ("andmed")

alamtükk2Suurus
4
Andmeala suurus
andmete suurus int

andmed
byteRate * heli kestus
Heliandmed
?

WAVE näide

Eelmist tabelit saab hõlpsasti tõlkida C-keele struktuuriks, kuid meie tänane keel on Python. Lihtsaim asi, mida saate teha, on kasutada "lainet" - mürageneraatorit. Selle ülesande jaoks ei vaja me kõrget baitide kiirust ja tihendamist.
Esmalt 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)

Järgmiseks peame tabelist looma kõik vajalikud muutujad nende suuruste järgi. Selles olevad muutujate väärtused sõltuvad ainult numSamples (proovide arv). Mida rohkem neid on, seda kauem meie müra jätkub.

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

Jääb vaid need vajalikus järjekorras üles 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)  # записываем

Ja nii, valmis. Skripti kasutamiseks peame lisama vajalikud käsurea argumendid:
python3 WAV.py [num of samples] [output]
proovide arv – loendamine. proovid
output — väljundfaili tee

Siin on link müraga testhelifailile, kuid mälu säästmiseks alandasin BPS-i 1b/s-ni ja kanalite arvu 1-ni (32-bitise tihendamata stereoheli vooga 64 kbs-ga selgus, et 80 MB puhast wav-faili ja ainult 10): https://instaud.io/3Dcy

Kogu kood (WAV.py) (Koodil on palju korduvaid muutujaväärtusi, see on vaid visand):

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

Summaarne

Nii olete õppinud natuke rohkem digitaalheli ja selle salvestamise kohta. Selles postituses me pakkimist (audioFormaat) ei kasutanud, kuid kõigi populaarsemate artiklite arvestamiseks on vaja 10 artiklit. Loodan, et õppisite enda jaoks midagi uut ja see aitab teid edasistes arengutes.
Tänan!

allikatest

WAV-faili struktuur
WAV – Vikipeedia

Allikas: www.habr.com

Lisa kommentaar