Metodi di compressione/archiviazione dei dati multimediali nei formati WAVE e JPEG, parte 1

Ciao! La mia prima serie di articoli sarà incentrata sull'analisi dei metodi di compressione e archiviazione delle immagini e del suono, come JPEG (immagine) e WAVE (suono), includendo anche esempi di programmi che utilizzano questi formati (.jpg, .wav) nella pratica. In questa parte esamineremo specificamente WAVE.

Storia

WAVE (Waveform Audio File Format) è un formato di file contenitore per memorizzare la registrazione di un flusso audio. Questo contenitore è solitamente utilizzato per conservare suoni non compressi in modulazione di impulsi codificati. (Fonte: Wikipedia)

È stato ideato e pubblicato nel 1991 insieme a RIFF da Microsoft e IBM (le aziende IT leader dell'epoca).

La struttura del file

Il file ha una parte intestazione, i dati stessi, ma non ha un piè di pagina. L'intestazione pesa in totale 44 byte.
Nell'intestazione sono presenti le impostazioni relative al numero di bit per campione, alla frequenza di campionamento, alla profondità del suono e ad 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/Panificazione
Valore (alcuni di essi sono fissi)

chunkId
4
Identificazione 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
Header rimanente (in byte)
16 per 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, utilizzeremo 1 canale (3/4/5/6/7… — traccia audio specifica, per esempio 4 per il quadrofoni, ecc.)

sampleRate
4
Frequenza di campionamento audio (in Hertz)
Maggiore è, migliore sarà la qualità del suono, ma richiederà più memoria per creare la traccia audio della stessa lunghezza, valore consigliato — 48000 (la qualità del suono 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 è, meglio e più pesante sarà l'audio, oltre i 32 bit non ci sono differenze per l'orecchio umano

subchunk2Id
4
Etichetta dell'inizio dei dati (poiché ci possono essere altri elementi di intestazione 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 il nostro linguaggio oggi è Python. La cosa più semplice da fare, utilizzando "onda", è generare 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)

Successivamente, dobbiamo creare tutte le variabili necessarie dalla tabella in base alle loro dimensioni. Le grandezze variabili dipendono qui solo da numSamples (numero di campioni). Più campioni ci saranno, 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'  # saranno sufficienti 2 canali (stereo)
sampleRate = pack('<L', 1000)  # 1000 va bene, ma se ne metti di più, il rumore sarà udibile meglio. Con 1000 suona come il vento
bitsPerSample = b'x20x00'  # 0d32
byteRate = pack('<L', 1000 * 2 * 4)  # sampleRate * numChannels * bitsPerSample / 8  (audio 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 registrarli 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)  # registriamo

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

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

Ecco il codice completo (WAV.py) (Il codice presenta molte duplicazioni di valori delle variabili, è solo una bozza):

from struct import pack  # conversione di oggetti py 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, poiché urandom non è disponibile in windows
from sys import argv, exit  # argomenti per il programma e uscita

if len(argv) != 3:  # +1 nome dello script (-1 se si congela)
    print('Uso: python3 WAV.py [numero 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 sono sufficienti (stereo) 
sampleRate = pack('<L', 1000)  # 1000 è sufficiente, ma si può anche aumentare.
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

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

Risultato

Ora hai appreso un po' di più sul suono digitale e su come viene memorizzato. In questo articolo non abbiamo utilizzato compressione (audioFormat), ma per esaminare ciascuno dei formati più popolari saranno necessarie 10 articoli. Spero che tu abbia imparato qualcosa di nuovo e che questo ti aiuti nei tuoi 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