Metodi di compressione/storage di dati multimediali in formati WAVE e JPEG, parte 1

Ciao! La mia prima serie di articoli sarà dedicata allo studio dei metodi di compressione e archiviazione di immagini/suoni, come JPEG (immagine) e WAVE (suono). Includerà anche esempi di programmi che utilizzano questi formati (.jpg, .wav) nella pratica. In questa parte esamineremo in particolare WAVE.

Storia

WAVE (Waveform Audio File Format) è un formato di file contenitore per la registrazione di flussi audio. Questo contenitore viene generalmente utilizzato per memorizzare audio non compresso in modulazione a impulsi codificati. (Tratto da Wikipedia)

È stato inventato e pubblicato nel 1991 insieme a RIFF dalle aziende Microsoft e IBM (Le principali aziende IT dell'epoca).

La struttura del file

Il file ha una parte header, i dati stessi, ma non ha footer. L'header pesa in totale 44 byte.
Nell'header ci sono impostazioni sul numero di bit per campione, frequenza di campionamento, profondità del suono e altre informazioni necessarie per la scheda audio. (Tutti i valori numerici della tabella devono essere registrati in ordine Little-Endian)

Nome del blocco
Dimensione del blocco (B)
Descrizione/Scopo
Valore (alcuni sono fissi

chunkId
4
Definizione del file come contenitore multimediale
0x52494646 in Big-Endian («RIFF»)

chunkSize
4
Dimensione dell'intero file senza chunkId e chunkSize
FILE_SIZE — 8

format
4
Definizione del tipo da RIFF
0x57415645 in Big-Endian («WAVE»)

subchunk1Id
4
Per far sì che il file occupi più spazio, continua il formato
0x666d7420 in Big-Endian («fmt «)

subchunk1Size
4
Rimanente header (in byte)
16 di default (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 stiamo considerando)

numChannels
2
Numero di canali
1/2, prenderemo 1 canale (3/4/5/6/7… — traccia audio specifica, ad esempio 4 per suono quad e così via.)

sampleRate
4
Frequenza di campionamento audio (in Hertz)
Più è alta, migliore sarà la qualità del suono, ma più memoria sarà richiesta per creare una traccia audio della stessa lunghezza, il valore raccomandato è 48000 (qualità audio più accettabile)

byteRate
4
Numero di byte per 1 secondo
sampleRate numChannels bitsPerSample (seguente)

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

bitsPerSample
2
Numero di bit per 1 campione (profondità)
Qualsiasi numero, multiplo di 8. Maggiore è, migliore sarà e più pesante sarà l'audio, da 32 bit in poi non ci sono differenze per l'orecchio umano.

subchunk2Id
4
Marca di inizio dei dati (poiché potrebbero esserci altri elementi nell'header a seconda dell'audioFormat)
0x64617461 in Big-Endian («data»)

subchunk2Size
4
Dimensione dell'area dati
dimensione data in int

data
byteRate * durata audio
Dati audio
?

Esempio con WAVE

La tabella precedente può essere facilmente tradotta in una struttura in C, ma la nostra lingua di oggi è Python. La cosa più semplice che possiamo fare utilizzando il "wave" è un generatore di rumore. Per questo compito non avremo bisogno di un alto byteRate e di compressione.
Per iniziare, 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)

Dobbiamo poi creare tutte le variabili necessarie dalla tabella in base alle loro dimensioni. Le variabili instabili all'interno dipendono solo da numSamples (numero di campioni). Più 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 canali vanno bene (stereo)
sampleRate = pack('<L', 1000)  # 1000 è sufficiente, ma se ne mettiamo di più, il rumore sarà migliore. A 1000 suona come vento
bitsPerSample = b'x20x00'  # 0d32
byteRate = pack('<L', 1000 * 2 * 4)  # sampleRate * numChannels * bitsPerSample / 8  (audio a 32 bit)
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)  # il rumore stesso

Rimane solo da scriverli nell'ordine necessario (come nella tabella):

with open(output_path, 'wb') as fh:
    fh.write(chunkId + chunkSize + Format + subchunk1ID +
            subchunk1Size + audioFormat + numChannels + 
            sampleRate + byteRate + blockAlign + bitsPerSample +
            subchunk2ID + subchunk2Size + data)  # scriviamo

E così, fatto. Per utilizzare lo script, dobbiamo aggiungere gli argomenti della riga di comando necessari:
python3 WAV.py [num di campioni] [output]
num di campioni — num. di campioni
output — percorso del file di output

Ecco un link a un file audio di test con rumore, ma per risparmiare memoria ho ridotto il BPS a 1b/s e il numero di canali a 1 (con un flusso audio stereo non compresso a 32 bit di 64kbs, ho ottenuto 80M di file .wav pulito, mentre con questo solo 10): https://instaud.io/3Dcy

Tutto il codice completo (WAV.py) (Il codice ha molte duplicazioni di valori delle variabili, è solo un abbozzo):

da struct import pack  # traducci del py-objects in tipi di base da C
from os import urandom  # funzione per leggere /dev/urandom, per windows:
# from random import randint
# urandom = lambda sz: bytes([randint(0, 255) for _ in range(sz)])  # lambda per windows, dato che urandom non c' in windows
from sys import argv, exit  # argomenti per il programma e uscita

if len(argv) != 3:  # +1 nome dello script (-1 se lo congelerete)
    print('Usage: python3 WAV.py [num di campioni] [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 canali saranno sufficienti (stereo)
sampleRate = pack('<L', 1000)  # 1000 basta, ma si pu anche di pi.
bitsPerSample = b'x20x00'  # 0d32
byteRate = pack('<L', 1000 * 2 * 4)  # sampleRate * numChannels * bitsPerSample / 8  (suono a 32 bit)
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)  # il rumore stesso

with open(output_path, 'wb') as fh:
    fh.write(chunkId + chunkSize + Format + subchunk1ID +
            subchunk1Size + audioFormat + numChannels + 
            sampleRate + byteRate + blockAlign + bitsPerSample +
            subchunk2ID + subchunk2Size + data)  # scriviamo il risultato nel file

Risultato

Ora sapete un po' di pi sul suono digitale e su come viene memorizzato. In questo post non abbiamo usato la compressione (audioFormat), ma per esaminare ognuna delle popolari serviranno circa dieci articoli. Spero che abbiate appreso qualcosa di nuovo e che questo vi aiuti nei futuri sviluppi.
Grazie!

Fonti

Struttura del file WAV
WAV — Wikipedia

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster