Estrarre informazioni da un file .wav
Il file .wav è un sottotipo del file RIFF.
Le informazioni generali di un file WAVE sono contenute nei primi 44 byte del file medesimo.
Il file WAVE è sostanzialmente composto da un solo blocco (chunk), a sua volta composto da due sottoblocchi: il primo blocco, appunto, formato da 44 byte contiene - come già detto - le informazioni relative alle caratteristiche del file; il secondo blocco contiene i dati audio veri e propri della forma d'onda digitalizzata.
Estrazione delle informazioni con le sole funzioni Gambas
Volendo estrarre le informazioni più importanti relative ad un file WAV mediante le sole risorse interne di Gambas, andremo a leggere alcuni byte spcifici appartenenti al primo sotto-blocco. Bisogna sottolineare che i le informzioni sono espresse all'interno del primo sotto-blocco in modalità little-endian. |1|
Formato audio
Le informazioni relative al formato audio sono contenute in little-endian nel 21° e nel 22° byte.
Numero di canali
Le informazioni relative al numero di canali del file WAV sono contenute in little-endian nel 23° e 24° byte (indice offset: 22 - 23).
Frequenza di campionamento
Le informazioni relative alla frequenza di campionamento del file WAV sono contenute in little-endian nel 25°, 26° 27° e 28° byte (indice offset: 24 - 25 - 26 - 27).
Risoluzione del campionamento
La risoluzione del campionamento audio rappresenta la gamma di valori che un campione audio può assumere. Una gamma più ampia conferisce ovviamente un campionamento più dinamico e ricco.
Le informazioni relative alla risoluzione in bit del campionamento del file WAV sono contenute in little-endian nel 35° e 36° byte (indice offset: 34 - 35).
La lunghezza del file
I file WAV contiene immediatamente dopo il primo sotto-blocco iniziale quattro byte che indicano la quantità di byte contenuta dal secondo sotto-blocco (quello formato dai veri e propri dati audio grezzi). Per conoscere la totale dimensione del file WAV, pertanto, basterà aggiungere a tale valore il numero 44 (che rappresentano i quarantaquattro byte che formano il primo sotto-blocco informativo del file).
Numero dei campioni (samples)
Il numero di campioni si ottiene con la seguente formula:
campioni = dimensione_dei_dati_grezzi_in_byte / block_align
Ottenere la durata del file wav
Per ottenere la durata dei dati audio wav, si possono adottare due modalità di calcolo:
La prima modalità prevede la seguente formula:
durata = (dimensione_dei_dati_grezzi_in_byte * 8) / (frequenza * nbit * canali)
La seconda modalità prevede la seguente formula:
durata = (dimensione_dei_dati_grezzi_in_byte / block_align) / frequenza
Esempi pratici
Per estrarre le suddette informazioni, potremo, dunque, realizzare alcuni codici come quelli che seguono.
Assegnando i dati letti dall'Header del file wav ai membri di un'apposita Struttura
Public Struct Header_Wav
riff As String ' tipo file "RIFF"
lung_rest As Integer ' lunghezza del resto del file = dimensione restante dell'header (36 byte) + lunghezza dei dati audio grezzi
wave As String ' sottotipo file "wav"
fmt As String ' inizio blocco "fmt " del file
lungh_fmt As Integer ' lunghezza del blocco "fmt"
pcm As Short ' formato MS PCM
canali As Short ' Canali audio di ucita (mono = 1, stereo = 2)
frequenza As Integer ' Frequenza di campionamento per secondo
byte_rate As Integer ' byte per secondo = frequenza * byte_campione
byte_campione As Short ' block align (byte per campione) = canali * bit_per_campione / 8
bit_campione As Short ' bit per campione (risoluzione del campionamento) = 8, 16 o 24
data As String ' "data"
lungh_dati As Integer ' lunghezza dei dati audio grezzi
End Struct
Public Sub Main()
Dim fileWAV As String = "/percorso/del/file.wav"
Dim wav As File
Dim hw As New Header_Wav
wav = Open fileWAV For Read
' Leggiamo ed assegnamo alla Struttura tutte le informazioni presenti nell'Header del file wav:
With hw
.riff = legge_stringa(wav)
.lung_rest = legge_intero(wav)
.wave = legge_stringa(wav)
.fmt = legge_stringa(wav)
.lungh_fmt = legge_intero(wav)
.pcm = legge_corto(wav)
.canali = legge_corto(wav)
.frequenza = legge_intero(wav)
.byte_rate = legge_intero(wav)
.byte_campione = legge_corto(wav)
.bit_campione = legge_corto(wav)
.data = legge_stringa(wav)
.lungh_dati = legge_intero(wav)
End With
wav.Close
End
Private Function legge_stringa(fl As File) As String
Dim s As String
Read #fl, s, 4
Return s
End
Private Function legge_intero(fl As File) As Integer
Dim i As Integer
Read #fl, i
Return i
End
Private Function legge_corto(fl As File) As Short
Dim sh As Short
Read #fl, sh
Return sh
End
Usando una variabile vettoriale di tipo Byte[] - 1
Potremo utilizzare un vettore di tipo Byte[] per contenere i dati del file wav:
Public Sub Main()
Dim percorsoFile As String
Dim fl As File
Dim j, b, formato, canali, risoluzione As Byte
Dim buf As New Byte[]
Dim frequenza, dimensione As Integer
percorsoFile = "/percorso/del/file.wav"
' Carica un file audio Wav:
fl = Open percorsoFile For Read
For j = 0 To 43
Read #fl, b
buf.Add(b)
Next
Print "== Caratteristiche del file: "; File.Name(percorsoFile); " ==\n"
' Rileva il formato audio (legge 2 byte):
formato = buf[21] * CInt(2 ^ 8)
formato = formato Or buf[20]
If formato = 1 Then
Print "Formato audio = PCM"
Else
Print "Formato audio = "; formato
Endif
' Rileva il numero di canali (legge 2 byte):
canali = buf[23] * CInt(2 ^ 8)
canali = canali Or buf[22]
Print "Numero canali = "; canali
' Rileva la frequenza di campionamento (legge 4 byte):
frequenza = buf[27] * CInt(2 ^ 24)
frequenza = frequenza Or buf[26] * CInt(2 ^ 16)
frequenza = frequenza Or buf[25] * CInt(2 ^ 8)
frequenza = frequenza Or buf[24]
Print "Frequenza = hrz "; frequenza
' Rileva la risoluzione del campionamento (legge 2 byte):
risoluzione = buf[35] * CInt(2 ^ 8)
risoluzione = risoluzione Or buf[34]
Print "Risoluzione = "; risoluzione; " bit"
' Rileva la quantità dei soli dati grezzi (legge 4 byte):
dimensione = buf[43] * CInt(2 ^ 24)
dimensione = dimensione Or buf[42] * CInt(2 ^ 16)
dimensione = dimensione Or buf[41] * CInt(2 ^ 8)
dimensione = dimensione Or buf[40]
Print "Quantità dei soli dati grezzi = "; dimensione; " byte"
' Calcola la durata del brano audio:
Print "Durata del brano: "; Date(0, 0, 0, 0, 0, 0, Fix((dimensione * 8) / (frequenza * risoluzione * canali)) * 1000)
fl.Close
End
Usando una variabile vettoriale di tipo Byte[] - 2
Per estrarre le informazioni possiamo anche utilizzare un Vettore di tipo Byte[]:
Public Sub Main()
Dim percorsoFile As String
Dim fl As File
Dim bb As Byte[]
Dim solo_dati, frequenza As Integer
percorsoFile = "/percorso/del/file.wav"
fl = Open percorsoFile For Read
bb = New Byte[44]
bb.Read(fl, 0, 44)
If bb.ToString(0, 4) <> "RIFF" Then Error.Raise("Il file caricato non è di tipo 'RIFF' !")
If bb.ToString(8, 4) <> "WAVE" Then Error.Raise("Il file caricato non è di tipo 'WAVE' !")
If bb.ToString(12, 4) <> "fmt " Then Error.Raise("Il file caricato non è di tipo 'fmt ' !")
Print "== Caratteristiche del file: "; File.Name(percorsoFile); " ==\n"
Print "Dimensioni totali del file: "; Stat(percorsoFile).Size; " byte"
solo_dati = Stat(percorsoFile).Size - 44
Print "Dimensione dei soli dati audio grezzi: "; solo_dati; " byte"
If bb[20] = 1 Then
Print "Formato audio: PCM"
Else
Print "Formato audio: "; bb[20]
Endif
Print "Canali di uscita audio: "; bb[22]
frequenza = Val("&" & Hex(bb[27]) & Hex(bb[26]) & Hex(bb[25]) & Hex(bb[24]))
Print "Frequenza di campionamento: Hz "; frequenza
Print "Byte rate: "; Val("&" & Hex(bb[31]) & Hex(bb[30]) & Hex(bb[29]) & Hex(bb[28])); " Bps"
Print "Risoluzione di campionamento a "; bb[34]; " bit"
Print "Durata del brano: "; Date(0, 0, 0, 0, 0, 0, Fix((solo_dati * 8) / (frequenza * bb[34] * bb[22])) * 1000)
fl.Close
End
Usando la funzione Seek
Potremo anche più semplicemente spostarci all'interno del flusso mediante la funzione Seek:
Public Sub Main()
Dim fileWAV As String
Dim fl As File
Dim formato, canali, risoluzione As Byte
Dim d As Short
Dim frequenza, dimensione As Integer
fileWAV = "/percorso/del/file.wav"
d = Instr(File.Load(fileWAV), "data")
' Carica un file audio Wav:
fl = Open fileWAV For Read
Print "== Caratteristiche del file: "; File.Name(fileWAV); " ==\n"
' Rileva il formato audio (legge 2 byte):
Seek #fl, 20
Read #fl, formato
If formato = 1 Then
Print "Formato audio = PCM"
Else
Print "Formato audio = "; formato
Endif
' Rileva il numero di canali (legge 2 byte):
Seek #fl, 22
Read #fl, canali
Print "Numero canali = "; canali
' Rileva la frequenza di campionamento (legge 4 byte):
Seek #fl, 24
Read #fl, frequenza
Print "Frequenza = hrz "; frequenza
' Rileva la risoluzione del campionamento (legge 2 byte):
Seek #fl, 34
Read #fl, risoluzione
Print "Risoluzione = "; risoluzione; " bit"
' Rileva la quantità dei soli dati grezzi (legge 4 byte):
seek #fl, d + 3
Read #fl, dimensione
Print "Quantità dei soli dati grezzi = "; dimensione; " byte"
' Calcola la durata del brano audio:
Print "Durata del brano: "; Date(0, 0, 0, 0, 0, 0, Fix((dimensione * 8) / (frequenza * risoluzione * canali)) * 1000)
fl.Close
End
Estrazione delle informazioni con le funzioni esterne del API di SOX
La libreria Sox contiene risorse per poter gestire ampiamente i file audio: riproduzione e registrazione, conversione di vari formati audio in altri formati, nonché applicazione di vari effetti.
Le risorse del API di Sox consentono anche di ottenere facilmente varie informazioni dai file audio, richiamando l'attuale versione della libreria: libsox.so.2.0.1
Potremo procedere con un codice simile al seguente:
Public Struct sox_signalinfo_t
rate As Float ' sox_rate_t: samples per second, 0 if unknown (typedef double sox_rate_t)
channels As Integer ' number of sound channels, 0 if unknown
precision As Integer ' bits per sample, 0 if unknown
length As Long ' samples * chans in file, 0 if unknown, -1 if unspecified (typedef unsigned long sox_uint64_t;)
mult As Pointer ' Effects headroom multiplier; may be null
End Struct
Public Struct sox_format_t
filename As Pointer
signal As Struct Sox_signalinfo_t
End Struct
Private sfIn As New Sox_format_t
Library "libsox:2.0.1"
Private Const SOX_SUCCESS As Byte = 0
' int sox_init(void)
' Client API: Initialize effects library. SOX_SUCCESS if successful.
Private Extern sox_init() As Integer
' sox_format_t * sox_open_read(char const *path, sox_signalinfo_t const *signal, sox_encodinginfo_t const *encoding, char const *filetype)
Private Extern sox_open_read(path As String, signalP As Pointer, encoding As Pointer, filetype As String) As Sox_format_t
Public Sub Main()
Dim err As Integer
Dim fileWAV As String
fileWAV = "/percorso/del/file.wav"
err = sox_init()
If err <> SOX_SUCCESS Then Error.Raise("Impossibile inizializzare la libreria 'libsox' !")
sfIn = sox_open_read(fileWAV, Null, Null, Null)
With sfIn
Print "File audio: "; String@(.filename)
Print "Frequenza di campionamento: hrz "; .signal.rate
Print "Numero di canali: "; .signal.channels
Print "Risoluzione campionamento: "; .signal.precision; " bit"
Print "Dimensione dei soli dati grezzi del file wav: "; .signal.length * .signal.channels; " byte" ' (tale valore è privo dei byte del blocco di intestazione del file wav = 44 byte)
End With
End
Note
[1] Ordine dei byte
Riferimenti
Per un ulteriore e dettagliata spiegazione rinviamo ai seguenti siti:
- http://en.wikipedia.org/wiki/Resource_Interchange_File_Format
- http://it.wikipedia.org/wiki/WAV
- https://ccrma.stanford.edu/courses/
- http://www.topherlee.com/software/pcm-tut-wavformat.html
- http://msdn.microsoft.com/en-us/library/windows/desktop/ee415713%28v=vs.85%29.aspx
- http://www.digitalpreservation.gov/formats/fdd/fdd000001.shtml
- http://unusedino.de/ec64/technical/formats/wav.html
- http://www-mmsp.ece.mcgill.ca/documents/AudioFormats/WAVE/WAVE.html
- http://sharkysoft.com/archive/lava/docs/javadocs/lava/riff/wave/doc-files/riffwave-frameset.htm
- http://www.johnloomis.org/cpe102/asgn/asgn1/riff.html
- http://www.lightlink.com/tjweber/StripWav/Canon.html
- http://paulbourke.net/dataformats/audio/