Mètodes per comprimir/emmagatzemar dades multimèdia en formats WAVE i JPEG, part 1

Hola! La meva primera sèrie d'articles se centrarà a estudiar els mètodes d'emmagatzematge i compressió d'imatge/àudio com ara JPEG (imatge) i WAVE (so), i també inclourà exemples de programes que utilitzen aquests formats (.jpg, .wav) a la pràctica. En aquesta part veurem WAVE.

Història

WAVE (format de fitxer d'àudio de forma d'ona) és un format de fitxer contenidor per emmagatzemar una gravació d'un flux d'àudio. Aquest contenidor s'utilitza normalment per emmagatzemar àudio modulat amb codi de pols sense comprimir. (Pres de la Viquipèdia)

Va ser inventat i publicat l'any 1991 juntament amb RIFF per Microsoft i IBM (Leading IT company of that time).

Estructura del fitxer

El fitxer té una part de capçalera, les dades en si, però no el peu de pàgina. La capçalera pesa un total de 44 bytes.
La capçalera conté la configuració del nombre de bits de la mostra, la freqüència de mostreig, la profunditat del so, etc. informació necessària per a la targeta de so. (Tots els valors de la taula numèrica s'han d'escriure en ordre Little-Endian)

Nom del bloc
Mida del bloc (B)
Descripció/Propòsit
Valor (per a alguns és fix

chunkId
4
Definició d'un fitxer com a contenidor multimèdia
0x52494646 a Big-Endian ("RIFF")

mida del tros
4
Mida de tot el fitxer sense chunkId i chunkSize
FILE_SIZE - 8

format
4
Definició de tipus de RIFF
0x57415645 a Big-Endian ("WAVE")

subchunk1Id
4
Perquè el fitxer ocupi més espai continuant amb el format
0x666d7420 a Big-Endian ("fmt")

subchunk1Size
4
Capçalera restant (en bytes)
16 per defecte (per al cas sense compressió de flux d'àudio)

format d'àudio
2
Format d'àudio (depèn del mètode de compressió i de l'estructura de dades d'àudio)
1 (per a PCM, que és el que estem considerant)

numChannels
2
Nombre de canals
1/2, agafarem 1 canal (3/4/5/6/7... - una pista d'àudio específica, per exemple 4 per a àudio quàdruple, etc.)

freqüència de mostreig
4
Freqüència de mostreig d'àudio (en Hertz)
Com més alt, millor serà el so, però com més memòria es necessitarà per crear una pista d'àudio de la mateixa durada, el valor recomanat és 48000 (la qualitat de so més acceptable)

byteRate
4
Nombre de bytes per segon
freqüència de mostreig numChannels bitsPerSample (més)

blockAlign
2
Nombre de bytes per a 1 mostra
numChannels * bitsPerSample: 8

bits per mostra
2
Nombre de bits per 1 mostra (profunditat)
Qualsevol nombre que sigui múltiple de 8. Com més gran sigui el nombre, millor i més pesat serà l'àudio, a partir de 32 bits no hi ha diferència per als humans.

subchunk2Id
4
Marca de referència de dades (ja que hi pot haver altres elements de capçalera segons el format d'àudio)
0x64617461 a Big-Endian ("dades")

subchunk2Size
4
Mida de l'àrea de dades
mida de les dades en int

dades
byteRate * durada de l'àudio
Dades d'àudio
?

Exemple WAVE

La taula anterior es pot traduir fàcilment a una estructura en C, però el nostre llenguatge d'avui és Python. El més fàcil que podeu fer és utilitzar una "ona": un generador de soroll. Per a aquesta tasca no necessitem byteRate i compressió elevats.
Primer, importem els mòduls necessaris:

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

A continuació, hem de crear totes les variables necessàries a partir de la taula segons les seves mides. Els valors variables que hi ha depenen només de numSamples (nombre de mostres). Com més n'hi hagi, més temps s'allargarà el nostre soroll.

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

Només queda anotar-los en l'ordre requerit (com a la taula):

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

I així, llestos. Per utilitzar l'script, hem d'afegir els arguments de línia d'ordres necessaris:
python3 WAV.py [num of samples] [output]
nombre de mostres - recompte. mostres
output — ruta al fitxer de sortida

Aquí hi ha un enllaç a un fitxer d'àudio de prova amb soroll, però per estalviar memòria vaig baixar el BPS a 1b/s i el nombre de canals a 1 (amb un flux d'àudio estèreo sense comprimir de 32 bits a 64 kbs, va resultar ser 80 M de fitxer .wav pur, i només 10): https://instaud.io/3Dcy

El codi sencer (WAV.py) (El codi té molts valors de variables duplicats, això és només un esbós):

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

Així que heu après una mica més sobre el so digital i com s'emmagatzema. En aquesta publicació no hem utilitzat compressió (format d'àudio), però per tenir en compte cadascun dels més populars, es necessitaran 10 articles, espero que hàgiu après alguna cosa nova per vosaltres mateixos i això us ajudi en futurs desenvolupaments.
Gràcies!

Fonts

Estructura de fitxers WAV
WAV - Viquipèdia

Font: www.habr.com

Afegeix comentari