Tere! Minu esimene artiklite seeria keskendub piltide ja heli kokkusurumise ja salvestamise meetodite uurimisele, nagu JPEG (pilt) ja WAVE (heli), ning toob näiteid programmide kohta, mis kasutavad neid formaate (.jpg, .wav) praktikas. Selles osas keskendume just WAVE-le.
Ajalugu
WAVE (Waveform Audio File Format) on failivorming, mis on mõeldud helivoo salvestamiseks. Seda konteinerit kasutatakse tavaliselt kokkusurumata heli salvestamiseks impulss-koodimodulatsioonis. (Võetud Vikipeediast)
See loodi ja avaldati 1991. aastal koos RIFF-iga Microsofti ja IBM-i poolt (toona juhtivad IT ettevõtted).
Faili struktuur
Failil on päis, andmed, kuid ei ole jalust. Päis kaalub kokku 44 baiti.
Päises on seadistused bitide arvu, proovivõtmise sageduse, helisügavuse jms jaoks, mis on vajalikud helikaardile. (Kõik numbrilised väärtused tabelis peavad olema kirjutatud Little-Endian järjekorras)
Bloki nimi
Bloki suurus (B)
Kirjeldus/Eesmärk
Väärtus (mõnedel on fikseeritud)
chunkId
4
Faili määramine meedia konteinerina
0x52494646 Big-Endian vormingus («RIFF»)
chunkSize
4
Kogu faili suurus ilma chunkId ja chunkSize'ita
FILE_SIZE — 8
format
4
Tüübi määramine RIFF-ist
0x57415645 Big-Endian vormingus («WAVE»)
subchunk1Id
4
Selleks, et fail võtaks rohkem ruumi, jätku format'i kirjeldus
0x666d7420 Big-Endian vormingus («fmt »)
subchunk1Size
4
Ülejäänud päis (baitides)
16 vaikimisi (kokkusurumata helivoo juhtumite jaoks)
audioFormat
2
Heli formaat (sõltub kokkusurumismeetodist ja heliandmete struktuurist)
1 (PCM jaoks, mida me praegu vaatame)
numChannels
2
Kanalite arv
1/2, võtame 1 kanali (3/4/5/6/7… — spetsiifiline helirada, näiteks 4 nelikanalist heliks jne)
sampleRate
4
Heli proovivõtmisfrekvents (Hertsides)
Mida suurem, seda parem on heli, kuid ka mälumaht, mis on vajalik sama pikkuse heliraja loomiseks, suureneb, soovitatav väärtus — 48000 (kõige vastuvõetavam helikvaliteet)
byteRate
4
Baitide arv ühe sekundi kohta
sampleRate numChannels bitsPerSample (edasi)
blockAlign
2
Baitide arv ühe proovi kohta
numChannels * bitsPerSample: 8
bitsPerSample
2
Bittide arv ühe proovi kohta (sügavus)
Igasugune number, mis on jagatav 8-ga. Mida suurem, seda parem ja raskem on heli, alates 32-bitist ei ole inimese jaoks erinevust
subchunk2Id
4
Andmete alguspunkti tähis (kuna heli formaadi järgi võivad olla teised päise elemendid)
0x64617461 Big-Endian vormingus («data»)
subchunk2Size
4
Andmealade suurus
data suurus int'ina
data
byteRate * audio pikkus
Audio andmed
?
Näide WAVE'ist
Eelmise tabeli võib hõlpsasti tõlkida C struktuuri, kuid meie keeleks on täna Python. Ainus, mida saab teha, kasutades "lainet", on müra generaator. Selle ülesande jaoks ei ole meil vaja kõrget byteRate'i ega tihendamist.
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 tabelist vastavalt nende suurusele. Muutumatud väärtused sõltuvad siin ainult numSamples'ist (näidiste arv). Mida rohkem neid on, seda kauem kestab meie müra.
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 piisab, kuid kui seadistada rohkem, siis müra on paremini kuulda. 1000-l kõlab nagu tuul
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) # müra endaleAlles, mis jääb üle, on need vajalikku järjestusse 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) # kirjutameJa 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 testimüra helifailile, kuid mälusäästu pärast vähendasin BPS-i 1b/s ja kanalite arvu 1-ni (32-bitise kompressimata stereo helivogtikus 64kbs puhul sai 80M puhta .wav faili, aga nüüd ainult 10):
Koodihulk (WAV.py) (Kood sisaldab palju korduvaid muutujaid, see on lihtsalt visand):
from struct import pack # py-objektide teisendamine C põhitüüpideks
from os import urandom # funktsioon lugemiseks \/dev\/urandom, Windowsi jaoks:
# from random import randint
# urandom = lambda sz: bytes([randint(0, 255) for _ in range(sz)]) # lambda Windowsi jaoks, kuna urandom'i Windowsis ei ole
from sys import argv, exit # argumentide edastamine programmile ja väljumine
if len(argv) != 3: # +1 skripti nimi (-1, kui kavatsete külmutada)
print('Kasutus: python3 WAV.py [samples arvu] [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 piisab, kuid võib olla ka rohkem.
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) # hale heli
with open(output_path, 'wb') as fh:
fh.write(chunkId + chunkSize + Format + subchunk1ID +
subchunk1Size + audioFormat + numChannels +
sampleRate + byteRate + blockAlign + bitsPerSample +
subchunk2ID + subchunk2Size + data) # salvestame faili tulemuseKokkuvõte
Nüüd teate veidi rohkem digitaalhelist ja kuidas seda salvestatakse. Selles postituses me ei kasutanud kompressiooni (audioFormat), kuid iga populaarse arutamiseks kulub umbes 10 artiklit. Loodan, et õppisite midagi uut ja see aitab teid tulevikus arendustes.
Aitäh!
Allikad
Allikas: habr.com
