Metodoj por kunpremi/stoki amaskomunikilajn datumojn en WAVE kaj JPEG-formatoj, parto 1

Saluton! Mia unua serio de artikoloj fokusiĝos pri studado de bildaj/aŭdaj kunpremado kaj konservado de metodoj kiel JPEG (bildo) kaj WAVE (sono), kaj ankaŭ inkluzivos ekzemplojn de programoj uzantaj ĉi tiujn formatojn (.jpg, .wav) praktike. En ĉi tiu parto ni rigardos WAVE.

История

WAVE (Ondoforma Audio File Format) estas ujo dosierformato por stoki registradon de sonfluo. Tiu ujo kutimas tipe stoki nekunpremitan pulskodon modulitan aŭdion. (Elprenita el Vikipedio)

Ĝi estis inventita kaj publikigita en 1991 kune kun RIFF fare de Mikrosofto kaj IBM (Gvidantaj IT-firmaoj de tiu tempo).

Dosiera strukturo

La dosiero havas kaplinion, la datumojn mem, sed neniun piedlinion. La kaplinio pezas entute 44 bajtojn.
La kaplinio enhavas agordojn por la nombro da bitoj en la specimeno, specimenofteco, sonprofundo ktp. informoj necesaj por la sonkarto. (Ĉiuj nombraj tabelvaloroj devas esti skribitaj en Little-Endian ordo)

Nomo de bloko
Grando de bloko (B)
Priskribo/Celo
Valoro (por iuj ĝi estas fiksita

chunkId
4
Difinante dosieron kiel amaskomunikilaron
0x52494646 en Big-Endian ("RIFF")

chunkSize
4
Grandeco de la tuta dosiero sen chunkId kaj chunkSize
FILE_SIZE - 8

Formato
4
Tipodifino de RIFF
0x57415645 en Big-Endian ("ONDO")

subparto1Id
4
Por ke la dosiero okupas pli da spaco kiel daŭrigo de la formato
0x666d7420 en Big-Endian ("fmt")

subpeco1Size
4
Resta kaplinio (en bajtoj)
16 defaŭlte (por la kazo sen kunpremado de audiofluo)

audioFormato
2
Audioformato (dependas de kunpremadmetodo kaj sondatumstrukturo)
1 (por PCM, kio estas kion ni pripensas)

numChannels
2
Nombro de kanaloj
1/2, ni prenos 1 kanalon (3/4/5/6/7... - specifan sontrakon, ekzemple 4 por kvara sono, ktp.)

specimenRate
4
Audio-specimena indico (en Hertz)
Ju pli alta, des pli bona estos la sono, sed ju pli da memoro estos bezonata por krei sonan trakon de la sama longo, la rekomendita valoro estas 48000 (la plej akceptebla sonkvalito)

byteRate
4
Nombro da bajtoj por sekundo
specimenRate numChannels bitsPerSample (plia)

blokAlign
2
Nombro da bajtoj por 1 specimeno
numChannels * bitsPerSample: 8

bitsPerSample
2
Nombro da bitoj per 1 specimeno (profundo)
Ajna nombro, kiu estas oblo de 8. Ju pli alta la nombro, des pli bona kaj pli peza estos la audio, de 32 bitoj ne estas diferenco por homoj.

subparto2Id
4
Datuma referencmarko (ĉar povas esti aliaj kapelementoj depende de la audioFormato)
0x64617461 en Big-Endian ("datenoj")

subpeco2Size
4
Grandeco de la areo de datumoj
grandeco de datumoj en int

datumoj
byteRate * sondaŭro
Sondatumoj
?

Ondo ekzemplo

La antaŭa tabelo facile tradukeblas en strukturon en C, sed nia lingvo hodiaŭ estas Python. La plej facila afero, kiun vi povas fari, estas uzi "ondon" - bruogenerilon. Por ĉi tiu tasko ni ne bezonas altan byteRate kaj kunpremadon.
Unue, ni importu la necesajn modulojn:

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

Poste, ni devas krei ĉiujn necesajn variablojn el la tabelo laŭ iliaj grandecoj. La variaj valoroj en ĝi dependas nur de numSamples (nombro de specimenoj). Ju pli da ili estas, des pli longe daŭros nia bruo.

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

Restas nur skribi ilin en la bezonata sinsekvo (kiel en la tabelo):

with open(output_path, 'wb') as fh:
    fh.write(chunkId + chunkSize + Format + subchunk1ID +
            subchunk1Size + audioFormat + numChannels + 
            sampleRate + byteRate + blockAlign + bitsPerSample +
            subchunk2ID + subchunk2Size + data)  # записываем

Kaj tiel, preta. Por uzi la skripton, ni devas aldoni la necesajn komandliniajn argumentojn:
python3 WAV.py [num of samples] [output]
nombro da specimenoj - kalkuli. specimenoj
eligo — vojo al la eligdosiero

Jen ligo al testa sondosiero kun bruo, sed por ŝpari memoron mi malaltigis la BPS al 1b/s kaj malaltigis la nombron da kanaloj al 1 (kun 32-bita nekunpremita stereofluo je 64kbs, ĝi montriĝis esti 80M de pura .wav dosiero, kaj nur 10): https://instaud.io/3Dcy

La tuta kodo (WAV.py) (La kodo havas multajn duplikatajn variajn valorojn, ĉi tio estas nur skizo):

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

La rezulto

Do vi lernis iom pli pri cifereca sono kaj kiel ĝi estas konservita. En ĉi tiu afiŝo ni ne uzis kunpremadon (audioFormat), sed por konsideri ĉiun el la popularaj, estos postulataj 10 artikoloj.Mi esperas, ke vi lernis ion novan por vi mem kaj ĉi tio helpos vin en estontaj evoluoj.
Dankon!

Fontoj

WAV-dosierstrukturo
WAV - Vikipedio

fonto: www.habr.com

Aldoni komenton