Sie sind auf Seite 1von 2

TD de programmation structure : 1re anne

2014-2015

TD n 6 : Traitement des signaux audio


Buts

: Allocation dynamique de tableau, pointeurs

Ce TP ncessite de se munir dun casque ou dcouteurs audio.


L'objectif de ce TP est de manipuler les pointeurs et l'allocation dynamique. Pour cela vous raliserez
diffrentes fonctions de traitement et de manipulation de signaux audio.
Le signal acoustique
Un signal acoustique (ou son) est produit par la vibration d'un corps provoquant des ondes qui se propagent dans l'air.
L'exemple le plus classique est la vibration de la membrane d'un haut parleur. Si un son est compos de vibrations
(frquences) dans la gamme de 20 20kHz alors il peut tre capt par l'oreille humaine. De plus, un son est entendu si
il est mit avec une intensit (amplitude) suffisante. Un son capt par l'oreille est de nature analogique (continu).
Pour enregistrer ce son sur support numrique (monde discret) le signal rel (en rouge figure 1) est chantillonn, c'est
dire qu'une valeur toutes les T secondes est enregistre (figure 2). Si la frquence d'chantillonnage fs =1/T est
suffisamment grande, le son peut tre restitu sans que cette approximation ne se peroive l'oreille.

unsigned char* get_son_wav_dyn(char* filename, int* freq, int* nb_samples) Chargement dun fichier wav filename dans un tableau. Sa frquence dchantillonnage freq et de sa taille
nb_samples sont passs par adresse et modifi par la fonction. Le tableau est allou par la fonction et retourn
par la fonction.

1 Reprsentation et lecture des sons


Votre premire tche sera de complter le fichier son.c en crivant les fonctions suivantes :
unsigned char* alloue_son(int nb_samples) qui prpare l'espace mmoire pour un son dont les
nb_samples chantillons sont des unsigned char. Toutes les valeurs du tableau allou seront initialises
0;
void libere_son(void* son) qui libre l'espace mmoire occup par le son.
Vous testerez ensuite ces fonctions dans le fichier test1.c qui utilise :
1. la rcupration dun son depuis un fichier .wav (get_son_wav)
2. la lecture dun son sous forme de tableau de unsigned char (joue_son)
3. La cration et lcoute dun nouveau son (dirac, signal continu)
Exemple de fichier test.c:
#include
#include
#include
#include

<stdlib.h>
<stdio.h>
<SDL_phelma.h>
"son.h"

int main(int argc, char * argv[]){


unsigned char* son=NULL;
unsigned char* son2=NULL;
unsigned char* son3=NULL;
char wavfile[] = "johnny.wav";
int i,nb_samples, fd, amplitude=200, freq;
SDL_Surface* f=NULL;
if (!Sound_Init()) fprintf(stderr,"init failed:%d.\n", Sound_GetError());
puts("Ecouter le son initial: tapez une touche"); getchar();
/* Jouer le son contenu dans le fichier*/
joue_son_wav(wavfile);

Figure 1 : son en rouge, periode T

Lechantillonnage du signal prcdent

Reprsentation des signaux audio


Pour simplifier le traitement des sons dans ce TP, un son est reprsent par un tableau doctets (unsigned
char) o chaque lment du tableau reprsente lintensit dun chantillon du signal sonore. Un seul canal est
considr (mono seulement). Les chantillons sont rangs dans l'ordre de leur acquisition. Le tableau correspondant au
signal de la figure 2 est :
i
t[i]

0
12

1
16

2
17

3
14

4
5

5
1

6
2

7
7

8
12

9
10

10
1

11
3

12
12

13
11

Lecture des fichiers audio


Les fichiers audio que vous aurez manipuler sont des fichiers .wav (WAVeform audio format). Pour lire ces
fichiers, vous utiliserez la bibliothque SDL laquelle nous avons ajout quelques fonctions pour simplifier ces
manipulations. Les fonctions utiles sont les suivantes :
int joue_son(unsigned char* son, int nb_samples, int frequence) - Envoie dun
son contenu dans le tableau de taille nb_samples sur la carte son du PC la frquence d'chantillonnage
frequence :
int affiche_joue_son(unsigned char* son, int nb_samples, int frquence,
SDL_Surface* f, int coul) idem joue_son, mais affiche en plus un graphique traant le signal
sonore dans la fentre f avec la couleur coul.
int joue_son_wav(char* filename) - Envoi dun fichier wav de nom filename sur la carte
son du PC

/* Lire un fichier son */


son = get_son_wav_dyn(wavfile,&freq,&nb_samples);
/* afficher ses caracteristiques*/
printf("fichier %s echantillons %d frequence %d\n",wavfile,freq,nb_samples);
puts("Ecouter le son recupere: tapez une touche"); getchar();
/* Crer une fenetre pour laffichage graphique */
f=newfenetregraphique(800,400);
/* Ecouter au casque */
if (f) affiche_joue_son(son, nb_samples, freq,f, SDL_PH_ROUGE);
else joue_son(son, nb_samples, freq);
/* Ecouter un train de Dirac : 44100Hz train 441 Hz*/
son2 = alloue_son(nb_samples);
freq = 44100; fd = freq/100;
for(i=0;i<nb_samples;i++) son2[i] = ((i%fd)==0)*amplitude;
printf("Signal en peigne de Dirac %d Hz: tapez une touche\n",fd); getchar();
joue_son(son2, nb_samples, freq);
libere_son(son2);
/* Ecouter un signal continu : meme nombre dechantillons*/
son3 = alloue_son(nb_samples);
for(i=0;i<nb_samples;i++) son3[i] = amplitude;
puts("Signal continu: tapez une touche"); getchar();
joue_son(son3, nb_samples, freq);

/* Liberer la memoire */
libere_son(son); libere_son(son3);
}
}

La compilation se fera laide du fichier Makefile donn sur le site internet.

2 Traitement simple du signal audio


Vous devrez crire les fonctions suivantes dans le fichier son.c et les tester avec le fichier test2.c avec les fichiers
wav donns et des signaux crs :
1. unsigned char max (unsigned char* son, int taille) qui renvoie la valeur max du son.
2. unsigned char* cree_sin(unsigned char amp, int taille, int freqsin, int
fs) qui cre un signal sinusodal d'amplitude amp, de frquence freqsin de taille taille avec une
frquence d'chantillonnage de fs. Le signal gnrer est donc :
s(i) = amp*sin(2*pi*freqsin*i/fs)
La fonction sin et la constante M_PI sont dfinies dans le fichier dentte math.h.
Attention au risque de division entire en C.
3. unsigned char* inverse(unsigned char* son, int taille) qui cre et retourne un son
dont les lments seront inverss dans le temps par rapport au son initial son. Le son invers sera stock dans
un tableau allou dynamiquement et retourn par la fonction inverse.
4. unsigned char* sous_echantillonnage_brutal(unsigned char* son, int taille,
int T) qui sous-chantillonne un son avec le taux T (entier). Cela veut dire qu'un chantillon tous les T
chantillons sera conserv dans le signal obtenu (figure 3). Le nouveau son sera cr dans un tableau allou
dynamiquement et retourn par cette fonction. Que se passe t il quand le sous chantillonnage est grand ?

3.1 Sources Audio la mme frquence


Lorsque les sources sont la mme frquence, les chantillons des 2 signaux sont synchroniss : le premier chantillon
est la date 0, le deuxime la date 0+1/f, .. le ime la date 0+i/f
On peut donc directement appliquer la formule prcdente sur les signaux t1 et t2.
Ecrire les fonctions de mixage suivantes :
1. void mixe_meme_frequence(unsigned char** pson, int *taille, unsigned char*
son1, int taille1, unsigned char* son2, int taille2) qui mixe deux sources audio
son1 et son2 de taille taille1 et taille2. La fonction alloue un espace mmoire pour le rsultat
dans *pson. Le parametre pson est un pointeur pass par adresse, car la valeur pointe est modifie par
lallocation.
2. Ecrire le programme principal qui teste cette fonction avec les fichiers wav donns.

3.2 Sources Audio des frquences diffrentes


Lorsque les sources sont des frquences diffrentes, les chantillons des 2 signaux s1 (* bleue) et s2 (o rouge) ne sont
en gnral plus synchroniss : le premier chantillon est la date 0 pour les 2 signaux, mais le deuxime la date 1/f1
pour s1 et 1/f2 pour s2, .. les ime la date i/f1 pour s1 et i/f2 pour s2. On ne peut donc pas faire doprations sur ces
chantillons.

Figure 4 : signaux frquences diffrentes


Une solution consiste r-chantillonner les signaux. En supposant que la frquence dchantillonnage f1 du signal f1
est la plus grande (T1 le plus petit), on r-chantillonne le signal s2 de manire ce quil soit chantillonn la
frquence f1 : le taux de r-chantillonnage est donc f1/f2. On va sur-chantillonner le signal s2.
On peut ensuite utiliser la fonction de mixage la mme frquence sur les signaux s1 et s2 sur-echantillonns.

figure 3 : sous echantillonage dun facteur 2.

3 Mixage de deux sources audio


La fusion de deux sources sonores est une des manipulations de base du traitement des signaux acoustiques. Si vous
coutez deux sources sonores, ce que vous entendez est en fait l'addition de ces deux sources. Cependant, le mixage
numrique de deux sources sonores est un peu plus complexe car il faut prendre en compte les diffrences de codage, de
compression et de frquence d'chantillonnage des signaux avant de les additionner. De plus, la simple addition peut
entraner un dpassement de la dynamique de valeur (integer overflow i.e. dpasser 255 dans le cas de nos signaux 8
bits). Le moyennage n'est pas non plus une solution car il aurait l'effet d'attnuer les deux sons. Dans le cas de valeurs
non signes, la formule suivante permet de mixer deux signaux audio s1 et s2 :
y(t) = (s1(t) + s2(t))*max(max(s1,n1),max(s2,n2))/(max(s1,n1)+max(s2,n2)).
o max(s,n) dsigne la valeur maximale du tableau s de taille n.
Pour raliser cette opration sans dbordement, il faut dabord stocker les valeurs des chantillons dans une variable de
type float puis convertir (caster) le rsultat en octet (unsigned char). La conversion se fait laide de loprateur (type).
Par exemple, pour raliser une moyenne, on crira : ce sera une instruction du type
float x1,x2,x3 ;
unsigned char c ;
x1 = t1[i] ; x2 = t2[i] ;
x3 = (x1 + x2 ) / 2 ;
c = (unsigned char) (x3) ;
ou plus directement
c = (unsigned char) ( ( (float)t1[i] + (float)t2[i])/2) ;
Ici, on convertit t1[i] en rel simple prcision, on convertit t2[i] en rel simple prcision, on ralise la somme des 2 en
simple prcision, on divise par 2 en simple prcision, et on termine en convertissant le rel en octet.

Travail raliser : crire les fonctions de mixage suivantes :


3. unsigned char* sur_echantillonnage_brutal(unsigned char* son, int taille,
int T) qui sur-chantillonne un son avec le taux T (entier). Cela signifie que pour tous les chantillons
originaux, T-1 chantillons doivent tre insrs par interpolation linaire. La taille totale du nouveau son est
donc T*(taille-1). Linterpolation linaire entre les chantillons i et i+T est donne par
resultat[i*T+j]=((T-1-j)*son[i]+j*son[i+1])/(T-1),
pour i compris entre 0 et taille-2 et j compris entre 0 et T-1. Le nouveau son sera cr dans un tableau
allou dynamiquement et retourn par cette fonction

figure 5 : Sur echantillonage dun facteur 2. Les points rouges sont obtenus par une interpolation linaire (une
droite) dfinie par leurs voisins
4.

void mixe(unsigned char **son, int *taille, int *frequence, unsigned char*
son1, int taille1, int frequence1, unsigned char* son2, int taille2, int
frequence2) qui mixe deux signaux sonores dont le rapport des frquences d'chantillonnage est entier
(i.e. max(frequence1,frequence2) = T*min(frequence1,frequence2) avec T entier). Le son rsultant doit avoir
la frquence la plus haute parmi celles des signaux d'entre. Rutiliser des fonctions dj codes est fortement
encourag...