Metodi per comprimere/archiviare dati multimediali nei formati WAVE e JPEG, parte 1

Ciao! La mia prima serie di articoli si concentrerà sullo studio dei metodi di compressione e archiviazione di immagini/audio come JPEG (immagine) e WAVE (suono) e includerà anche esempi di programmi che utilizzano questi formati (.jpg, .wav) nella pratica. In questa parte esamineremo WAVE.

storia

WAVE (Waveform Audio File Format) è un formato di file contenitore per archiviare la registrazione di un flusso audio. Questo contenitore viene generalmente utilizzato per memorizzare audio modulato con codice a impulsi non compresso. (Tratto da Wikipedia)

È stato inventato e pubblicato nel 1991 insieme a RIFF da Microsoft e IBM (le principali società IT dell'epoca).

Struttura dei file

Il file ha una parte di intestazione, i dati stessi, ma nessun piè di pagina. L'intestazione pesa complessivamente 44 byte.
L'intestazione contiene le impostazioni per il numero di bit nel campione, frequenza di campionamento, profondità del suono, ecc. informazioni necessarie per la scheda audio. (Tutti i valori della tabella numerica devono essere scritti in ordine Little-Endian)

Nome del blocco
Dimensione del blocco (B)
Descrizione/scopo
Valore (per alcuni è fisso

pezzoId
4
Definizione di un file come contenitore multimediale
0x52494646 in Big-Endian (“RIFF”)

dimensione del pezzo
4
Dimensione dell'intero file senza ChunkId e ChunkSize
DIMENSIONE_FILE - 8

formato
4
Definizione del tipo da RIFF
0x57415645 in Big-Endian (“WAVE”)

sottochunk1Id
4
In modo che il file occupi più spazio come continuazione del formato
0x666d7420 in Big-Endian (“fmt”)

sottochunk1Dimensione
4
Intestazione rimanente (in byte)
16 per impostazione predefinita (per il caso senza compressione del flusso audio)

audioFormat
2
Formato audio (dipende dal metodo di compressione e dalla struttura dei dati audio)
1 (per PCM, che è quello che stiamo considerando)

numCanali
2
Numero di canali
1/2, prenderemo 1 canale (3/4/5/6/7... - una traccia audio specifica, ad esempio 4 per quad audio, ecc.)

frequenza di campionamento
4
Frequenza di campionamento audio (in Hertz)
Più è alto, migliore sarà il suono, ma maggiore sarà la memoria necessaria per creare una traccia audio della stessa lunghezza, il valore consigliato è 48000 (la qualità del suono più accettabile)

byteRate
4
Numero di byte al secondo
frequenza di campionamento numCanali bitsPerSample (ulteriore)

blockAlign
2
Numero di byte per 1 campione
numChannels * bitsPerSample: 8

bitPerSample
2
Numero di bit per 1 campione (profondità)
Qualsiasi numero che sia multiplo di 8. Più alto è il numero, migliore e più pesante sarà l'audio, da 32 bit per l'uomo non c'è differenza

sottochunk2Id
4
Segno di riferimento dei dati (poiché potrebbero esserci altri elementi di intestazione a seconda dell'audioFormat)
0x64617461 in Big-Endian("dati")

sottochunk2Dimensione
4
Dimensioni dell'area dati
dimensione dei dati in int

dati
byteRate * durata dell'audio
Dati audio
?

Esempio ONDA

La tabella precedente può essere facilmente tradotta in una struttura in C, ma il nostro linguaggio per oggi è Python. La cosa più semplice che puoi fare è usare un'"onda", un generatore di rumore. Per questo compito non abbiamo bisogno di byteRate e compressione elevati.
Per prima cosa importiamo i moduli necessari:

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

Successivamente, dobbiamo creare tutte le variabili necessarie dalla tabella in base alle loro dimensioni. I valori delle variabili in esso contenuti dipendono solo da numSamples (numero di campioni). Più ce ne sono, più a lungo durerà il nostro rumore.

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

Non resta che trascriverli nella sequenza richiesta (come da tabella):

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

E quindi, pronto. Per utilizzare lo script, dobbiamo aggiungere gli argomenti necessari della riga di comando:
python3 WAV.py [num of samples] [output]
numero di campioni - conteggio. campioni
output: percorso del file di output

Ecco un collegamento a un file audio di prova con rumore, ma per risparmiare memoria ho abbassato i BPS a 1b/s e ho abbassato il numero di canali a 1 (con un flusso audio stereo non compresso a 32 bit a 64kbs, si è rivelato essere 80M di file .wav puri e solo 10): https://instaud.io/3Dcy

L'intero codice (WAV.py) (Il codice ha molte duplicazioni di valori variabili, questo è solo uno schizzo):

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

risultato

Quindi hai imparato qualcosa in più sul suono digitale e su come viene archiviato. In questo post non abbiamo utilizzato la compressione (audioFormat), ma per considerare ciascuno di quelli più diffusi saranno necessari 10 articoli. Spero che tu abbia imparato qualcosa di nuovo per te stesso e questo ti aiuterà negli sviluppi futuri.
Grazie!

fonti

Struttura del file WAV
WAV-Wikipedia

Fonte: habr.com

Aggiungi un commento