Il formato WAV
Cos'è e cosa contiene un Wav
Il nome dell'estensione, wav, è l'inizio della parola Waveform, che significa "forma d'onda": infatti, i file di questo tipo non
sono altro che contenitori per una certa onda rappresentante un segnale audio. Al contrario degli mp3, spiegati nelle precedenti lezioni
di questa sezione, i file wav sono, di norma, loseless, ossia non comportano la perdita di dati. Come avevo spiegato in precedenza,
la codifica degli mp3, oltre ad un potente algoritmo di compressione (algoritmo di Huffman), fa anche uso di tecniche euristiche per
rintracciare ed eliminare frequenze inutili, in quanto non percepibili dall'orecchio umano. Nei wav questo non succede: il segnale viene
preservato nella sua interezza e proprio per questa sua caratteristica viene impiegato in ambiti specifici, come il DSP (Digital Signal
Processing), che, dovendo analizzare a fondo un segnale, deve disporre di tutte le informazioni possibili, anche di quelle apparentemente
insignificanti. Ne fanno uso anche utenti molto amanti della musica per cercare di ottenere e riprodurre con la massima fedeltà
possibile ogni pezzo.Anche se esistono diversi tipi di codifica, alcuni compressi, altri no, il tipo in assoluto più utilizzato è il PCM. Esso è, inoltre, il più semplice, poiché prevede il salvataggio dell'onda così come viene ottenuta dal dispositivo di registrazione: non si deve applicare nessun filtro, nessuna trasformazione, niente di complicato, bisogna solo trascrivere i dati, linearmente, sul file. In questo tutorial mi occuperò dei file wave pcm.
I chunks
Il formato wav è un sottogruppo del più generale formato RIFF (Resource Interchange File Format). Quest'ultimo prevede che i
file siano strutturati secondo una struttura formata da chunk, ossia blocchi di dati. Ognuno di questi blocchi deve essere contraddistinto
da un id (identificativo), formato da 4 lettere in codifica ascii standard (quella a 128 bit), immediatamente seguito da una
serie di 4 bytes che indicano la dimensione dell'intero chunk (esclusi questi 8 bytes di intestazione). Lo si può, quindi, schematizzare
come segue:| Struttura di un chunk | ||
| Id | Dimensione | Contenuto |
| 4 bytes | 4 bytes | [Dimensione] bytes |
Prima di procedere, è importantissimo dire che tutte le dimensioni e i numeri interi sono codificati secondo il modello little-endian, ed è necessario agire di conseguenza quando si leggono o si scrivono dati di questo tipo. Sebbene abbia già introdotto il concetto di endianness nei Fondamenti di Informatica, lo ripeto brevemente anche qui. Secondo il modello big-endian, i bits più significativi si trovano all'inizio, perciò il numero
Quest'ultima è anche la notazione convenzionale e più logica usata per esprimere un numero in base 2. Nell'esempio ho aggiunto degli 0 all'inizio per far notare che la rappresentazione utilizza 2 bytes di spazio. Nel modello little-endian, invece, i bits più significativi vanno posti alla fine, perciò i due bytes usati per rappresentare normalmente quel numero saranno invertiti:
11265 sarebbe il risultato ottenuto leggendo dal file i due bytes
Il RIFF chunk
Il primo e più importante chunk da conoscere, ed anche il primo presente nella struttura del file, è RIFF. Questo chunk contiene
praticamente tutti gli altri, ossia costituisce da solo tutto il file: viene usato, inoltre, per riconoscere il formato RIFF, poiché
nel caso lo stream non presenti questo blocco di dati iniziali sicuramente non sarà un file riff. Il suo id, ovviamente, è "RIFF"
(tutto maiuscolo, mi raccomando), mentre la sua dimensione è uguale alla grandezza del file meno 8. I primi 8 bytes, infatti,
sono l'header del chunk stesso, 4 per l'id e 4 per la dimensione. Dopo questi primi bytes viene una parola di 4 lettere che identifica
il tipo di risorsa codificata: poiché si tratta di un file audio, nel nostro caso sarà "WAVE" (sempre maiuscolo).| Struttura del RIFF chunk | |||
| Id ("RIFF") | Dimensione | Tipo di risorsa ("WAVE") | Contenuto |
| 4 bytes | 4 bytes | 4 bytes | [Dimensione - 4] bytes |
Potete scaricare questo campione audio per controllare con un editor esadecimale tutto quello che andrò a spiegare tra poco.
Il Format chunk
Questo chunk serve a comunicare tutte le informazioni generale sull'onda e su come riprodurla. Il suo id è "fmt " (tutto minuscolo,
e seguito da uno spazio), e la sua dimensione è, generalmente, 16. Il contenuto di questo blocco consiste in una serie di numeri
interi, variabilmente da 2 o da 4 bytes, che indicano alcuni parametri molto importanti. Nell'ordine in cui si trovano, sono:
- Codice di compressione (2 bytes) : indica quale formato è stato usato per codificare l'informazione audio. Può assumere i seguenti
valori:
- 0 = Sconosciuto
- 1 = PCM (non compresso, ossia quello più diffuso, che utilizziamo in questo tutorial)
- 2 = Microsoft ADPCM
- 6 = ITU G.711 a-law
- 7 = ITU G.711 Au-law
- 17 = IMA ADPCM
- 20 = ITU G.723 ADPCM (Yamaha)
- 49 = GSM 6.10
- 64 = ITU G.721 ADPCM
- 80 = MPEG
- 65536 = Sperimentale
- Numero di canali (2 bytes) : indica quanti canali usa il file audio. I valori standard sono 1 (mono) e 2 (stereo), sebbene ne siano virtualmente supportati oltre 65000
- Sample rate (4 bytes) : indica quanti campioni vengano riprodotti in un secondo. Un campione non è altro che un "punto" dell'onda, ossia del segnale audio contenuto nel file. Illustrerò più nello specifico il concetto di campione nel paragrafo dedicato al Data chunk
- Bytes processati al secondo (4 bytes) : indica quanti bytes di informazione vengono riprodotti al secondo. È un valore direttamente derivato sa sample rate, poiché basta moltiplicare il sample rate per la dimensione del sample in bytes per ottenere questo numero. Quindi, è opzionale leggerlo dal file, in quanto vi si può risalire indirettamente per vie traverse
- Allineamento blocchi (o Dimensione blocchi) (2 bytes) : indica la dimensione di un campione (blocco o sample), in bytes. Anche questo valore è calcolabile, e dopo vedremo come
- Bits per campione (2 bytes) : indica quanti bits vengano usati per salvare un sample, e gli unici valori che può assumere sono 8, 16, 24 o 32
| Struttura del Format chunk | |||||||
| Id "fmt " |
Dimensione 16 o più |
Codice | Numero canali | Sample rate | Bytes / secondo | Dimensione blocchi | Bits / sample |
| 4 bytes | 4 bytes | 2 bytes | 2 bytes | 4 bytes | 4 bytes | 2 bytes | 2 bytes |
| Appendice opzionale del Format chunk | |
| Dimensione | Altre informazioni |
| 2 bytes | x bytes |
Il Data chunk
Eccoci arrivati al cuore del file wave: il data chunk. Esso contiene il vero e proprio segnale audio. Il suo id è "data", e la dimensione
varia a seconda della lunghezza del segnale audio.| Struttura del Data chunk | |||
| Id ("data") | Dimensione | Segnale audio | |
| 4 bytes | 4 bytes | x bytes | |
Il segnale audio riproduce un'onda discreta. Come ho spiegato nell'introduzione al formato mp3, il suono si trasmette attraverso onde meccaniche che sono date dall'oscillazione di punti dello spazio, nella fattispecie dalla compressione o dall'espansione dell'aria. Nella realtà, l'onda è continua, ossia il suo grafico è una funzione continua. Poiché una funzione continua implica un numero di punti infinito, è ovvio che la macchina non può contenere e memorizzare alcun segnale di questo tipo. Può simularlo prendendo tanti punti, vicinissimi, in modo che l'onda risultante sembri continua. Come ho già detto, con una frequenza di campionamento di 44100Hz l'orecchio umano non riesce a percepire la differenza tra un segnale digitale (discreto) e uno analogico (continuo). Ciò detto, se rappresentiamo l'onda su un grafico, un campione (o sample) contiene l'ascissa di ogni punto:

Ogni quadratino è un campione dell'onda discreta. Ora, l'onda era molto semplice, e la frequenza di campionamento molto bassa (ammettiamo 20Hz), tuttavia il principio è lo stesso. Avremo, quindi, a che fare con onde del genere:

Che rappresenta un segnale continuo di questo tipo:

In questi esempi, l'ampiezza dell'onda in ogni punto era compresa tra valori dell'ordine delle decine. I campioni contengono ampiezze variabili, che cambiando di entità a seconda di bits per campione. Se, infatti, un campione ha a disposizione 8 bits, potrà assumere valori tra -127 e +127; se ne ha a disposizione 16, potrà andare da -32'768 a +32'768; se ne ha a disposizione 24, da -8'388'608 a +8'388'608; se ne ha a disposizione 32, da -2'147'483'648 a +2'147'483'648. Logicamente, poiché al variare dell'ampiezza varia il volume del suono, ed i volumi massimo e minimo sono fissi, all'aumentare dei bits concessi ad ogni sample, aumenterà la precisione dell'onda digitale: essa approssimerà meglio la funzione continua (a parità di frequenza di campionamento). Con questo, bisogna fare alcune precisazioni:
- E' possibile ridurre la qualità di un suono riducendo il numero di bits assegnati ad ogni sample, ma non è possibile aumentarla semplicemente aumentando questa quantià. Anche disponendo di un numero maggiore di "variazioni", i singoli campioni saranno solo una riproduzione in scala più dettagliata della stessa onda discreta. Se si passa a un valore bits/sample minore, diminuirà sensibilmente anche la dimensione del file
- Anche i valori dei sample sono scritti in little-endian, ma a differenza dei numeri presenti nei chunk precedenti, questi possono
assumere valori negativi. E qui cambia ancora il modo di intendere la codifica: infatti, per indicare il segno si usa il primo bit
(quello più significativo), e lo si imposta a 1 se si tratta di un valore negativo, o a 0 se positivo. In realtà il processo
che si attua non è solo un cambiamento del primo bit, ma coinvolge tutti i bits: per indicare un numero negativo, infatti, si
prende la rappresentazione binaria del suo valore assoluto e si applica l'operatore Not, ossia si invertono tutti i bit. Ad esempio,
per rappresentare -300 dovremmo prima prendere il suo valore assoluto (300) in binario:
300 =00000001 00101100 =01 2C
Quindi applicare l'operatore Not:Not 00000001 00101100 =11111110 11010011 =FE D3
E infine invertire l'ordine dei bytes per aderire al modello little endian:-300 =11010011 11111110 =D3 F3 =54'270
In pratica, se l'ultimo bytes, in little-endian, è un numero maggiore di 127 (il che implica che il suo primo bit sia 1), allora tutto il numero rappresentato è negativo, e per ottenerne il valore bisogna eseguire i passaggi inversi a quelli mostrati.
| 16 Bits per sample - Stereo | |||
| Sample 1 | Sample 2 | ||
| Left | Right | Left | Right |
| 12'457 | 9'126 | 12'870 | 9'340 |
| 8 Bits per sample - Mono | |
| Sample 1 | Sample 2 |
| Left | Left |
| 127 | -7 |
A questo punto sarà facile intuire come calcolare l'allineamento dei blocchi, ossia la dimensione, in bytes, di ogni sample:
Allineamento = Canali * Bits per sample / 8Infatti, un byte è costituito da 8 bits, e in un campione ci sono tanti valori quanti i canali: ecco che otteniamo questo magnifico numero. Prendendo la dimensione del Data chunk (numero di bytes dell'informazione audio), dividendola per l'allineamento (numero di bytes per campione), otteniamo il numero di campioni che costituiscono l'onda; se dividiamo quest'ultimo per il sample rate (numero di samples riprodotti al secondo), otterremo la durata del brano, in secondi.
The Totem's Lair - Copyright (C) 2009
È vietata la riproduzione sia totale che parziale del sito.



