Metode de comprimare/stocare a datelor media în formatele WAVE și JPEG, partea 1

Buna ziua! Prima mea serie de articole se va concentra pe studierea metodelor de compresie și stocare a imaginii/audio, cum ar fi JPEG (imagine) și WAVE (sunet), și va include, de asemenea, exemple de programe care folosesc aceste formate (.jpg, .wav) în practică. În această parte ne vom uita la WAVE.

Poveste

WAVE (Waveform Audio File Format) este un format de fișier container pentru stocarea unei înregistrări a unui flux audio. Acest container este de obicei folosit pentru a stoca audio modulat cu cod de impulsuri necomprimat. (Preluat de pe Wikipedia)

A fost inventat și publicat în 1991 împreună cu RIFF de Microsoft și IBM (leading IT company of the time).

Structura fișierului

Fișierul are o parte antet, datele în sine, dar fără subsol. Antetul cântărește în total 44 de octeți.
Antetul conține setări pentru numărul de biți din eșantion, rata de eșantionare, adâncimea sunetului etc. informațiile necesare pentru placa de sunet. (Toate valorile numerice ale tabelului trebuie scrise în ordinea Little-Endian)

Nume bloc
Dimensiunea blocului (B)
Descriere/Scop
Valoare (pentru unii este fix

chunkId
4
Definirea unui fișier ca container media
0x52494646 în Big-Endian („RIFF”)

chunkSize
4
Dimensiunea întregului fișier fără chunkId și chunkSize
FILE_SIZE - 8

format
4
Definiția tipului din RIFF
0x57415645 în Big-Endian („WAVE”)

subchunk1Id
4
Pentru ca fișierul să ocupe mai mult spațiu continuând formatarea
0x666d7420 în Big-Endian („fmt”)

subchunk1Size
4
Antet rămas (în octeți)
16 în mod implicit (pentru cazul fără compresie a fluxului audio)

audioFormat
2
Format audio (depinde de metoda de compresie și structura datelor audio)
1 (pentru PCM, ceea ce luăm în considerare)

numChannels
2
Număr de canale
1/2, vom lua 1 canal (3/4/5/6/7... - o pistă audio specifică, de exemplu 4 pentru audio quad etc.)

rata simpla
4
Rata de eșantionare audio (în Herți)
Cu cât mai mare, cu atât sunetul va fi mai bun, dar cu cât va fi necesară mai multă memorie pentru a crea o pistă audio de aceeași lungime, valoarea recomandată este 48000 (cea mai acceptabilă calitate a sunetului)

byteRate
4
Numărul de octeți pe secundă
rata simpla numChannels bitsPerSample (mai departe)

blockAlign
2
Numărul de octeți pentru 1 probă
numChannels * bitsPerSample: 8

bitsPerSample
2
Număr de biți per 1 probă (adâncime)
Orice număr care este un multiplu de 8. Cu cât numărul este mai mare, cu atât audio va fi mai bun și mai greu; de la 32 de biți nu există nicio diferență pentru oameni

subchunk2Id
4
Marca de referință pentru date (deoarece pot exista și alte elemente de antet în funcție de formatul audio)
0x64617461 în Big-Endian(„date”)

subchunk2Size
4
Dimensiunea zonei de date
dimensiunea datelor în int

de date
byteRate * durata audio
Date audio
?

Exemplu WAVE

Tabelul anterior poate fi tradus cu ușurință într-o structură în C, dar limbajul nostru de astăzi este Python. Cel mai ușor lucru pe care îl puteți face este să folosiți un „undă” - un generator de zgomot. Pentru această sarcină nu avem nevoie de byteRate și compresie ridicate.
Mai întâi, să importăm modulele necesare:

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

În continuare, trebuie să creăm toate variabilele necesare din tabel în funcție de dimensiunile acestora. Valorile variabile din acesta depind numai de numSamples (numărul de mostre). Cu cât sunt mai mulți dintre ei, cu atât zgomotul nostru va dura mai mult.

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

Tot ce rămâne este să le scrieți în ordinea necesară (ca în tabel):

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

Și așa, gata. Pentru a folosi scriptul, trebuie să adăugăm argumentele necesare în linia de comandă:
python3 WAV.py [num of samples] [output]
num of samples - count. mostre
output — calea către fișierul de ieșire

Iată un link către un fișier audio de testare cu zgomot, dar pentru a economisi memorie am scăzut BPS-ul la 1b/s și am redus numărul de canale la 1 (cu un flux audio stereo necomprimat pe 32 de biți la 64 kb, s-a dovedit a fi 80 M de fișier .wav pur și doar 10): https://instaud.io/3Dcy

Întregul cod (WAV.py) (Codul are o mulțime de valori variabile duplicate, aceasta este doar o schiță):

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

Total

Așa că ați învățat puțin mai multe despre sunetul digital și despre cum este stocat. În această postare nu am folosit compresia (audioFormat), dar pentru a lua în considerare fiecare dintre cele populare vor fi necesare 10 articole.Sper că ai învățat ceva nou pentru tine și asta te va ajuta în evoluțiile viitoare.
Vă mulțumim!

surse

Structura fișierului WAV
WAV - Wikipedia

Sursa: www.habr.com

Adauga un comentariu