programmation [C]





le format wave

Il existe un très grand nombre de formats audio utilisés dont certains sont des formats propriétaires et d'autres libres de droits. Parmi ces formats, on distingue 2 grandes familles :
- Les formats sans perte de données (WAV, AIFF, FLAC …).
- Les formats avec compression de données. Pour un espace de stockage réduit, le signal audio est simplifié selon des algorithmes de compression avec pour coût une faible perte de qualité par rapport au signal initial (MP3, AAC…).

Pour une intégration simplifiée dans notre programme, nous avons choisi le format « WAVE » pour son entête basique et son absence de compression.



Structure d’un fichier « WAVE »

On distingue 2 parties dans un fichier WAVE : l’entête et la partie DATA. L’entête mesure 56 octets et contient les informations sur le format utilisé pour encoder le son (nombre d’octets par point échantillonné, nombre de canaux, fréquence d’échantillonnage, longueur du fichier…).
La partie DATA commence à l’octet 57 et contient toutes les valeurs des points échantillonnés. Comment ouvrir un fichier WAVE dans un programme [C] ?
Il nous faut créer une structure (WAVFILE dans notre programme) rigoureusement identique à celle de l’entête d’un fichier WAVE.
Charger le fichier dans le programme avec la commande « fopen ».
Dans le programme notre fichier audio est appelé WAV. On remarque aussi l’argument « rb » (read binary) dans la commande « fopen ». Le « b » est ajouté pour permettre au compilateur [C] de Windows de faire la distinction entre un fichier « TXT » et un fichier de données brutes. Lecture de l’entête et initialisation du Header par la commande « fread ».
« fread » est une fonction permettant la lecture de données brutes (binaires) d’un fichier dans un programme [C]. Il y a 4 paramètres à indiquer :
-La variable où les données seront enregistrées (tableau, structure ou variable simple)
-Taille des données à lire (en nombre d’octets)
-Nombre de fois que doit être effectuée la lecture
-Fichier à lire
Dans notre cas le programme va lire une fois le fichier WAV. Sa lecture se fera sur la taille du HEADER (correspondant à celle de l’entête) et les données lues seront enregistrées dans le HEADER.
Le HEADER ainsi rempli contient toutes les caractéristiques de notre fichier WAVE, notamment la fréquence d’échantillonnage (header.frequency), le nombre de canaux (header.channels), le nombre d’octets par échantillon (header.bytes_by_capture), le nombre d’octets dans la partie DATA (header.bytes_in_data).
Les fichiers WAVE utilisés seront des mono (header.channels = 1).
On peut alors déterminer le nombre d’échantillons contenus dans la partie DATA (=header.bytes_in_data / header.bytes_by_capture).
Pour la suite de notre programme, « taille » correspondra à la puissance de 2 supérieure au nombre total d’échantillons du fichier WAVE lu.
Création d’un tableau dynamique qui contiendra les échantillons.
On utilisera un tableau de type malloc, car la taille du tableau ne pourra être déterminée qu’après exécution du programme.
Ce tableau est une matrice à 2 dimensions de 2 colonnes et « taille » lignes.
Il s’agit d’un tableau de nombres complexes, car nous verrons par la suite que l’algorithme de TF utilisé dans nos autres programmes d'analyse nécessite un tableau de cette forme. Enfin nous allons remplir ce tableau de nouveau avec la fonction « fread ».
Enfin ce tableau sera enregistré dans un fichier data

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>

struct wavfile //définit la structure de l entete d un wave
{
char id[4]; // doit contenir "RIFF"
int totallength; // taille totale du fichier moins 8 octets
char wavefmt[8]; // doit etre "WAVEfmt "
int format; // 16 pour le format PCM
short pcm; // 1 for PCM format
short channels; // nombre de channels
int frequency; // frequence d echantillonage
int bytes_per_second; // nombre de octets par secondes
short bytes_by_capture; // nombre de bytes par echantillon
short bits_per_sample; // nombre de bit par echantillon
char data[4]; // doit contenir "data"
int bytes_in_data; // nombre de bytes de la partie data
};
main()
{
int i=0;
int taille=1; //variable qui correspondra par la suite a la longueur du tableau(puissance de 2)
int nbech=0; //nombre d echantillons extraits du fichier audio
char fichieraudio[100];
printf ("entrer le nom du fichier audio a analyser :\n");
scanf("%s", fichieraudio);
printf ("nom du fichier : %s\n", fichieraudio);
FILE *wav = fopen(fichieraudio,"rb"); //ouverture du fichier wave
struct wavfile header; //creation du header
if ( wav == NULL )
{
printf("\nne peut pas ouvrir le fichier demande, verifier le nom\n");
printf("ne pas oublier l'extention .wav\n");
exit(1);
}
//initialise le header par l'entete du fichier wave
//verifie que le fichier possÈde un entete compatible
if ( fread(&header,sizeof(header),1,wav) < 1 )
{
printf("\nne peut pas lire le header\n");
exit(1);
}
if ( header.id[0] != 'R'
|| header.id[1] != 'I'
|| header.id[2] != 'F'
|| header.id[3] != 'F' )
{
printf("\nerreur le fichier n'est pas un format wave valide\n");
exit(1);
}
if (header.channels!=1)
{
printf("\nerreur : le fichier n'est pas mono\n");
exit(1);
}
nbech=(header.bytes_in_data/header.bytes_by_capture);
printf ("\nle fichier audio contient %d echantillons\n",nbech);
while (nbech>taille)
{
taille=taille*2;
puissance=puissance+1;
}
double **tab=NULL; //tableau de l'onde temporelle
tab=malloc( (taille) * sizeof(double));
if (tab == NULL)
{
exit(0);
}
for(i=0;i<(taille);i++)
{
tab[i]=malloc( 2 * sizeof(double));
if (tab[i] == NULL)
{
exit(0);
}
}
i=0;
short value=0;
FILE *dat=fopen("data.dat","w"); //fichier data des echantillons
while( fread(&value,(header.bits_per_sample)/8,1,wav) )
{ //lecture des echantillons et enregistrement dans le tableau
tab[i][0]=value;
i++;
}
printf("\nnombre d'echantillons lus : %d\n",i);
printf("nombre de valeurs sauvegardees %d\n",i);
for (i=0;i<taille;i++)
{
fprintf(dat,"%lf %lf\n", tab[i][0], tab[i][1]);
}
for(i=0;i<taille;i++)
{
free(tab[i]);
tab[i] = NULL ;
}
fclose(wav);
fclose(dat);
}
télécharger le code source


Nos programmes

Modes d'une pièce

Nous cherchons à calculer les premiers modes d'une pièce parallélépipédique dont nous connaissons les dimensions à l'aide de la formule de la partie théorique, afin de prédire la réponse impultionnelle de la salle étudiée.
fig18 figure 10 : Illustration de la densité modale en fonction de la fréquence (simulations numériques traitant la salle étudiée dans les expériences 1 et 2)
télécharger le code source



Analyse spectrale

Pour une analyse efficace de nos echantillons sonores lors de nos diverses expériences nous avons eu recours à un algorythme de Transformée de Fourier issus de "Numerical Reciepies in C". En effet un tel algorythme nous permet de traiter les données fréquencielles d'un son dans un logiciel de tableur comme celui que nous utilisons : "Origine Pro 9". On peut trouver un outils d'analyse spectrale dans le logiciel gratuit Audacity mais la résolution offerte n'est pas suffisante pour une mesure du RT60 par l'analyse de la largeur de pics modaux.
figure 23 figure 12 : Mesure de la largeur d'un pic modal à -3dB
télécharger le code source



Longueur moyenne en 2D

Le but de ce programme est de vérifier la formule permettant de déterminer le parcours moyen d'un rayon entre deux réflections dans une salle rectangulaire (salle en deux dimensions) :
formule 15 AvecP le périmetre de la pièce rectangulaire en m et S la surface en m². télécharger le code source



Filtre Welch

Il s'agit d'une version améliorée de l'algorythme de Transformée de Fourier précédent où le programme effectue la TF d'un son, puis multiplie le résultat par une porte (fonction mathématique) pour enfin effectuer une Transformée de Fourier inverse. Afin de limiter les résidus introduits par la Transfomée de Fourier inverse, nous utilisons une fenêtre de type Welch centrée sur la fréquence du filtre passe bande souhaité :
formule 22 AvecN le nombre de points constituant la largeur de la fenêtre.

figure 28 figure 13 : Allure des différents types de fenêtre pour filtrage passe bande

figure 29 figure 14 : Allure de la TF d'un delta de Dirac par les fenêtres présentant des résidus (lobes)
On remarque que lorsque l'on utilise une fenêtre Welch par rapport à une porte "carrée" on a une diminution des résidus lors d'une transformée de Fourier ou sa transformée inverse.
télécharger le code source
On peut aussi accéder à un autre projet de licence "programmation d'un spectrogramme"

Sommaire

la Théorie

Toutes les notions nécéssaires à la compréhention de la réverbération en acoustique des salles.

Programmation [C]

Comment traiter un son en langage de programmation [C].

Expérience 1

Mesure de l'aire effective moyenne d'une personne.

Expérience 2

Analyses fréquentielles de la réverbération par stimulation modale d'une salle.