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 stessoRimane 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) # scriviamoE 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):
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 fileRisultato
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
Fonte: habr.com
