View Full Version : [C] leggere un file wave bit a bit
Ciao a tutti, ho un problema che mi assilla da qualche giorno, e non riesco a trovare la soluzione... :help:
Vorrei leggere un file .wav bit a bit, proprio fisicamente come è scritto sull'hdd, ma ho problemi quando si tratta di andare a leggere il file, il massimo che riesco a fare è un fread() ma praticamente mi vengono letti dei numeri interi positivi (che credo sia codice ascii ma non sono sicuro)...
Ho provato anche ad aprire il file in modalità binaria con fopen(file,"rb") ma niente da fare, non cambia nulla...
C'è qualcuno che potrebbe dirmi come implementare una cosa del genere con C ?? (Io utilizzo Dev-Cpp per lo sviluppo)
Grazie 1000 anticipatamente :D
Vorrei leggere un file .wav bit a bit, proprio fisicamente come è scritto sull'hdd, ma ho problemi quando si tratta di andare a leggere il file, il massimo che riesco a fare è un fread() ma praticamente mi vengono letti dei numeri interi positivi (che credo sia codice ascii ma non sono sicuro)...
Ciao, allora ... per cominciare bisognerebbe innanzitutto sapere che cosa ci vuoi fare con un file wave. Se vuoi solo fare un "dump" dei dati binari nel file, se vuoi estrarre i campioni audio per riprodurli con una scheda audio, ecc...
Sappi che non è comunque banalissimo leggere un file wave (ma neanche complicatissimo!). I file wave hanno un formato e una struttura ben precisa. I dati e le informazioni contenute in un file wave sono suddivise in quelli che vengono chiamati chunk. Un tipico file wave ha in genere come minimo 3 chunks: un chunk iniziale di tipo "RIFF" che è la testata del file, un altro chunk di tipo "fmt" che contiene le informazioni generali sui campioni audio e un terzo chunk di tipo "data" che contiene i campioni audio veri e propri.
Le specifiche del formato wave le trovi facilmente su internet. Ecco alcuni link:
1) Wotsit (http://www.wotsit.org) (cerca poi lì dentro il formato wave)
2) Wave Files - The Sonic Spot (http://www.sonicspot.com/guide/wavefiles.html)
3) WAVE PCM soundfile format (http://ccrma.stanford.edu/courses/422/projects/WaveFormat/)
A questo punto capisci che bisognerebbe sapere cosa vuoi fare di preciso.
Ci sono sostanzialmente almeno 2 possibilità per estrarre i dati da un file wave. La prima è quella di imparare le specifiche e la struttura dei file wave ed usare le funzioni di base dell'I/O (fopen, fread, ecc...) per leggere e decodificare i dati contenuti nel file. La seconda è quella di utilizzare funzioni o meglio librerie già fatte per leggere i dati in un file wave.
La Microsoft mette a disposizione una serie di funzioni Win32 per gestire proprio i file audio. Tutta la documentazione relativa alla gestione dei multimedia file in Win32 la trovi a partire da questo link:
MSDN - Multimedia File I/O (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_multimedia_file_i_o.asp)
Ho provato anche ad aprire il file in modalità binaria con fopen(file,"rb") ma niente da fare, non cambia nulla...
La funzione fread() non è influenzata dal modo di apertura ("r" o "rb") specificato nella fopen. fread legge sempre un blocco di byte e basta.
L'idea è venuta fuori ad un mio amico che deve stendere una monografia che fa vedere come da un file .wav si può campionarlo e quantizzarlo emulando praticamente quello che è un mp3, con la teoria studiata ai corsi di Elaborazione dei Segnali...
Praticamente se non ho capito male a lui servirebbe lo stream di bit da campionare e quantizzare, però da quanto ho capito da quello che mi hai scritto i file wav hanno una struttura particolare...
Ok allora dò un'occhiata e ti faccio sapere...
Ma in particolare, poi avendo a portata di mano la struttura del file wav e il formato con cui devo leggere i dati, quindi poi va bene l'uso di fread() giusto?
Ho dato meglio un'occhiata alla fread e ho letto che è fatta così
size_t fread(void *ptr, size_t dim, size_t nmemb, FILE *stream)
solo che non riesco a capire bene come devo usare "dim" e "nmemb"...ho anche spulciato fra gli header, ma ho trovato niente sullo struct "size_t"...
Ma in particolare, poi avendo a portata di mano la struttura del file wav e il formato con cui devo leggere i dati, quindi poi va bene l'uso di fread() giusto?
Se hai scelto la strada "fai da te" ;) , va benissimo usare la funzione fread().
Ho dato meglio un'occhiata alla fread e ho letto che è fatta così
size_t fread(void *ptr, size_t dim, size_t nmemb, FILE *stream)
solo che non riesco a capire bene come devo usare "dim" e "nmemb"...ho anche spulciato fra gli header, ma ho trovato niente sullo struct "size_t"...
Innanzitutto size_t non è una struttura. È un tipo di dato (definito in genere con una typedef negli include della libreria standard del "C") che rappresenta un numero intero senza segno. È il tipo restituito dall'operatore sizeof.
Per quanto riguarda la fread() ed anche l'opposta fwrite(), i due parametri dim e nmemb servono per specificare alle due funzioni quanti byte leggere/scrivere secondo la seguente formula: dim * nmemb;
In pratica leggono un certo numero di blocchi specificato da nmemb, ognuno di lunghezza dim. Nota importante: le due funzioni restituiscono un valore di tipo size_t che indica il numero di elementi letti. Se metti dim=10 e nmemb=2 e la fread va a buon fine cioè legge 20 bytes, ti restituisce 2 (non 20!!).
Ti faccio subito un esempio per la lettura del primo blocco RIFF del file wave (nota, presuppongo l'utilizzo in Windows, poiché userò dei tipi di dati definiti nel Platform SDK di Windows):
#pragma pack(1)
typedef struct
{
CHAR id[4];
DWORD length;
} RIFF_HEADER;
#pragma pack()
RIFF_HEADER riff_hdr;
size_t len_read;
len_read = fread (&riff_hdr, 1, sizeof (RIFF_HEADER), f);
if (len_read == sizeof (RIFF_HEADER))
{
..ok lettura corretta..
}
Perfetto...
Cercando in internet sulla base di quello che mi hai scritto ho trovato questo codice...
void Load_Wave_File(char *fname)
{
FILE *fp;
fp = fopen(fname,"rb);
if (fp)
{
BYTE id[4], *sound_buffer; //four bytes to hold 'RIFF'
DWORD size; //32 bit value to hold file size
short format_tag, channels, block_align, bits_per_sample; //our 16 values
DWORD format_length, sample_rate, avg_bytes_sec, data_size, i; //our 32 bit values
fread(id, sizeof(BYTE), 4, fp); //read in first four bytes
if (strcmp(id, "RIFF"))
{ //we had 'RIFF' let's continue
fread(&size, sizeof(DWORD), 1, fp); //read in 32bit size value
fread(id, sizeof(BYTE), 4, fp); //read in 4 byte string now
if (strcmp(id,"WAVE"))
{ //this is probably a wave file since it contained "WAVE"
fread(id, sizeof(BYTE), 4, fp); //read in 4 bytes "fmt ";
fread(&format_length, sizeof(DWORD),1,fp);
fread(&format_tag, sizeof(short), 1, fp); //check mmreg.h (i think?) for other
// possible format tags like ADPCM
fread(&channels, sizeof(short),1,fp); //1 mono, 2 stereo
fread(&sample_rate, sizeof(DWORD), 1, fp); //like 44100, 22050, etc...
fread(&avg_bytes_sec, sizeof(short), 1, fp); //probably won't need this
fread(&block_align, sizeof(short), 1, fp); //probably won't need this
fread(&bits_per_sample, sizeof(short), 1, fp); //8 bit or 16 bit file?
fread(id, sizeof(BYTE), 4, fp); //read in 'data'
fread(&data_size, sizeof(DWORD), 1, fp); //how many bytes of sound data we have
sound_buffer = (BYTE *) malloc (sizeof(BYTE) * data_size); //set aside sound buffer space
fread(sound_buffer, sizeof(BYTE), data_size, fp); //read in our whole sound data chunk
}
else
printf("Error: RIFF file but not a wave file\n");
}
else
printf("Error: not a RIFF file\n");
}
}
L'ho provato e sembra funzionare, dovrebbero esserci solo un pò di errori nell'allocazione dello stream in memoria, adesso debuggo un pochettino e cerco di farlo andare...
Domanda extra:
E se volessi sentire questo file wave, (una volta streamizzato in memoria) dovrei andare ad utilizzare funzioni proprie di windows perchè sarebbe praticamente impossibile scrivere da zero una procedura che faccia riprodurre alla scheda audio lo stream giusto?
Grazie ancora!
Perfetto...
Cercando in internet sulla base di quello che mi hai scritto ho trovato questo codice...
....
L'ho provato e sembra funzionare, dovrebbero esserci solo un pò di errori nell'allocazione dello stream in memoria, adesso debuggo un pochettino e cerco di farlo andare...
Questo sorgente non so ovviamente dove l'hai preso ma mi sembra un tantino semplicciotto, nel senso che è buttato giù alla buona. Fa troppe fread per prendersi ogni singolo campo di informazione (non ha senso così). Mi viene quasi da pensare che chi l'ha scritto non conosca le struct. :D
Il mio consiglio è quello di creare delle strutture per gestire gli header dei chunk. Poi comunque fai tu ...
Domanda extra:
E se volessi sentire questo file wave, (una volta streamizzato in memoria) dovrei andare ad utilizzare funzioni proprie di windows perchè sarebbe praticamente impossibile scrivere da zero una procedura che faccia riprodurre alla scheda audio lo stream giusto?
Grazie ancora!
Windows offre il supporto per "suonare" i file wave a diversi livelli. Il modo più semplice per riprodurre un file wave è quello di utilizzare la funzione <PlaySound> (http://msdn.microsoft.com/library/en-us/multimed/htm/_win32_playsound.asp). Puoi passargli direttamente il nome del file wave e lui ... te lo suona!
Altrimenti c'è l'approccio a basso livello. Tu leggi e decodifichi il file per conto tuo e tramite un sistema di buffering basato su callback/eventi puoi passare a Windows i campioni da riprodurre man mano che servono.
Tenersi tutto l'intero file wave in memoria va bene solo se il file è davvero piccolo. Tieni presente infatti che i file wave sono abbastanza "spreconi" in fatto di spazio occupato, proprio perché tipicamente non sono compressi.
Comunque trovi tutte le informazioni <qui> (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_multimedia_audio.asp).
...
fread(id, sizeof(BYTE), 4, fp); //read in first four bytes
if (strcmp(id, "RIFF"))
{ //we had 'RIFF' let's continue
...
Anzi ... tra l'altro è pure sballatissimo! Viene usata la strcmp che presuppone che id sia una stringa terminata dal carattere nullo, cosa che invece non è vera. E il test è comunque sbagliato. Come minimo si dovrebbe fare:
if (strncmp (id, "RIFF", 4) == 0)
Anzi ... tra l'altro è pure sballatissimo! Viene usata la strcmp che presuppone che id sia una stringa terminata dal carattere nullo, cosa che invece non è vera. E il test è comunque sbagliato. Come minimo si dovrebbe fare:
if (strncmp (id, "RIFF", 4) == 0)
Sì sì hai ragione, infatti prima di farlo funzionare ho dovuto fare un bel pò di debugging e capire come funzionava il programma (e la struttura del WAVE)...
Comunque ora sono riuscito a mettere in memoria il mio bel file wave, proprio come volevo, adesso mi metterò a pensare come posso realizzare una funzione a basso livello per "suonare" la memoria :D
Grazie ancora andbin, sei un mago :yeah:
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.