TP de traitement numérique du signal en temps réel

publicité
TP de traitement numérique
du signal en temps réel
B. Miramond – Université de Cergy-Pontoise
Dans ce TP nous allons implémenter quelques algorithmes de traitements de signal audio sur
la carte EZ-KIT Lite ADSP-21061. La plupart des algorithmes seront codés en C. Nous nous
basons pour cela sur le patron de programme qui vous a été fourni précédemment. Injecter un
signal audio sur l’entrée du codec (entrée INPUT de la carte), et récupérer le signal traité sur
la sortie OUTPUT en lui raccordant des écouteurs.
Dans Project->Options->Compile ne pas cocher sur la case Enable Optimisation. (Pas
d’optimisation des codes C).
I. Mono à stéréo, balance stéréo, doubleur, écho
a) Premier algorithme
Réaliser les commandes de mixage mono-stéréo et de balance stéréophonique données par les
schémas suivants :
Sortie
droite
Entrée
mono
Commande de
panoramique
Kd=1-Kg
Commande
de balance
0<Kg<1
•
•
Kd=1-Kg
0<Kg<1
Sortie
gauche
•
Sortie
droite
Entrée
droite
Entrée
gauche
Sortie
gauche
Retrouver dans le patron de code C fourni comment enregistrer un nouveau handler
d’interruption, comment paramétrer la période du timer et comment activer/désactiver/
complémenter la valeur des FLAG. Quel est le type C d’un handler d’interruption ?
Configurer le codec de la carte pour une fréquence d’échantillonnage de 44100 Hz, et
tester l’algorithme sur carte pour une seule valeur de Kd. Faire les calculs avec des
virgules flottantes, puis avec des virgules fixes.
On veut maintenant tester l’algorithme pour différentes valeurs du gain Kd. Afin de
mieux différentier les signaux de sortie, la variation du gain se fait par une interruption
IRQ1 matérialisée par un appui sur le switch S2 de la carte. A chaque appuie sur le
bouton S2 on lira une nouvelle valeur de Kd dans un tableau. Lorsque le tableau est
parcouru intégralement, l’index reviendra à zéro. Configurer pour cela les registres
concernés (IRQ1 activé sur front et non pas sur niveau, assigner IRQ1 à une fonction
d’interruption, …). Tester le code avec des variations linéaires du gain, puis
logarithmique (vous pouvez utiliser pour cela les fichiers .dat fournis sur la page web
du module).
•
Faites en sorte que le clignotement des leds suive le schéma suivant : la led FLAG3 de
la carte clignote avec une période d’une seconde pour témoigner du bon
fonctionnement du programme, FLAG1 change d’état à chaque appuis sur le switch
S2 et FLAG2 change d’état après avoir balayer toutes les valeurs de Kd.
b) Doubleur, génération d’écho
•
Réaliser l’effet de doubleur (dédoublement du signal) donné par les deux schémas
suivants. Dans le premier schéma, un signal mono est transformé en un signal pseudostéréo, où le premier canal reçoit le signal direct, le deuxième canal le signal retardé
avec un retard court (entre 10 et 30 ms). Cet effet tend à provoquer un déplacement
dans l’espace de l’image stéréophonique, comme si le musicien n’était pas placé
devant l’auditeur mais sur le côté. Dans le second schéma, le doubleur s’obtient en
mélangeant le signal retardé avec le signal original.
Doubleur(1)
Entrée
Z–n
Doubleur(2)
Sortie droite (Sd)
Sortie
Entrée
Z–n
+
Sortie gauche (Sg)
Fonction de transfert
Sd = z–n E et Sg = E
Fonction de transfert
S = (1+z–n) E
Implémentation
sd(k) = e(k–n) et sg(k) = e(k)
Implémentation
s(k) = e(k) + e(k–n)
Note : n∗Te est le temps de retard entre les deux signaux, avec Te la période
d’échantillonnage (ici Te=1/44100 sec).
Pour réaliser le doubleur, un buffer de taille d’au moins n échantillons e est nécessaire. On
vous demande de tester le code avec plusieurs valeurs du temps de retard (de 0 à 50 ms). Pour
cela vous ferez varier le temps de retard avec l’interruption IRQ1 de la même façon que
précédemment. Faire les calculs avec des virgules flottantes et avec des virgules fixes. S’il y a
des erreurs de type « out of memory », modifier dans le fichier de l’éditeur de lien les tailles
des segments de façon à inclure le buffer.
•
Apporter maintenant les modifications nécessaires pour réaliser le phénomène d’écho
à un signal audio :
Entrée
Sortie
+
K
Fonction de transfert
S/E = 1/(1 – K z–n)
Z–n
Implémentation
s(k) = e(k) + K s(k–n)
Vous pouvez remarquer que le buffer contient cette fois-ci l’historique du signal de sortie et
non pas de l’entrée.
II. Trémolo
a) Génération d’un signal périodique
La génération d’un signal sinusoïdale peut se faire par la fonction sin(2 f t ) =
sin(2 f Fe ᅲk ) = sin( ᅲk ) , avec t = k Te, et ∆ ϕ l’incrément élémentaire de phase.
Cependant, la fonction de sinus, étant coûteuse en temps de calcul, elle est généralement
évitée. Nous générons donc le signal sinusoïdal par une table d’échantillons pré calculés,
comme indiqué dans le cours. Notons qu’avec la même méthode, il est possible de générer un
signal périodique quelconque en modifiant seulement les valeurs de la table.
Vous pouvez générer la table d’échantillons soit par une fonction exécutée à l’initialisation
(début de la fonction main), soit à partir d’un fichier pré rempli contenant les données de la
table (en virgule fixe ou flottante). Vous choisissez une de ces deux méthodes.
Voici une manière de générer un signal périodique en C :
Soit M = 2m la taille de la table pouvant être adressée par m bits (la résolution de la phase du
signal est alors de 2π/M).
Pour avoir une phase avec une résolution plus élevée, on définit un compteur x codé sur n
bits, avec n > m. Une période est alors codée avec N = 2n valeurs.
signal
A chaque période d’échantillonnage, le nombre x est incrémenté de x , qui doit correspondre
à une incrémentation de phase ∆ ϕ . Par une simple règle de trois on obtient :
N
∆x =
∆ϕ = N ⋅ f / Fe , avec f la fréquence du signal souhaitée. L’adresse de la table
2π
correspondra alors aux m bits du poids fort du compteur x.
Le pseudo code de la fonction de génération d’un signal périodique est comme suit :
- calcul de l’incrément x
- x = x + x (circulaire, sur n bits)
- indice_table = m bits du poids fort de x
- retourne tableau[indice_table]
Cette fonction est appelée à chaque période d’échantillonnage.
Tester l’algorithme (en C) avec une fréquence variable (500 à 1000 Hz) et une table de 128
valeurs (m = 7) et un compteur x de n = 28 bits.
Tester l’algorithme avec un tableau de taille réduite (32 valeurs seulement). Que entendezvous, le signal vous paraîtra sûrement plus riche en fréquences. Pourquoi (que pouvez vous
dire de leurs spectres)? Comment résoudre ce problème tout en ayant une table de 32 valeurs?
Comparer la méthode que vous avez codée avec celle donnée en cours (en assembleur). Si
vous avez le temps, tester les deux codes (en C et en assembleur) et faire des tests de profiling
pour mesurer et comparer le nombre de cycles de chacune.
a) Trémolo
Sortie
droite
Entrée
droite
fréquence
amplitude
Oscillateur
TBF
Kd=1-Kg
0<Kg<1
Entrée
gauche
Sortie
gauche
L’algorithme ressemble à celui du premier algorithme. Le gain Kd est cependant un signal
périodique de basse fréquence.
Implémenter l’effet de Trémolo avec une fréquence variable entre 0 et 5 Hz.
III. Filtrage
Nous allons maintenant appliquer au signal audio un filtre passe bande, afin d’entendre que la
bande fréquentielle souhaitée. Les filtres numériques utilisés sont de type FIR (filtre à réponse
impulsionnelle finie), ayant une fonction de transfert :
S (z)
H ( z) 
 c0  c1 z 1  K  cn z  n
E( z)
avec c0 ,…, cn les coefficients du filtre et n l’ordre du filtre.
Implémentation :
s (k )  c0 e(k )  c1e(k  1)  K  cn e(k  n)
Vous allez effectuer alors, à chaque période d’échantillonnage, un produit de convolution
entre la réponse impulsionnelle du filtre (c0 ,…, cn) et les n+1 dernières échantillons du signal
audio.
Ecrire le code en C. Plusieurs fichiers .h vous sont fournis, chacun correspondant à un ordre
donné du filtre (4, 10, 20, 100, 200), chacun inclue les coefficients de trois filtres de bandes
passantes [500, 1k], [4.5k, 5k] et [9.5k, 10k]. Ces coefficients sont soit en virgule flottante ou
en virgule fixe. Les coefficients des filtres sont calculés sous Matlab, avec la fonction c =
fir1(n,[fc1,fc2]); avec fc1 et fc2 les fréquences de coupures d’une bande passante.
Pour tester le bon fonctionnement du filtre, assurer un clignotement de la led FLAG1 ou
FLAG2 à chaque 41000/2 appel à la fonction de traitement des échantillons, ce qui permet un
clignotement d’une période d’une seconde en cas de bon fonctionnement du filtre, il devrait
clignoter en synchronisme avec la led FLAG3 qui est liée au timer. Augmenter l’ordre du
filtre jusqu’à perdre cette synchronisation. Expliquer pourquoi?
Prendre le filtre d’ordre le plus élevé qui assure un fonctionnement correct. Mesurer le
nombre de cycles et le temps de traitement du traitement (sachant que le processeur
fonctionne à 40MHz, à 40MIPS et une instruction par cycle – voir le datasheet de
l’ADSP21061 et la fréquence du quartz sur la carte). Conclure?
Optimisation du code :
• Remplacer le code C par la fonction fir (voir le fichier filters.h) fournie par sharc et
tester des ordres élevés du filtre. Comprendre les paramètres de la fonction fir. Vous
pouvez remarquer que les pointeurs vers le vecteur des coefficients et le vecteur des
derniers échantillons sont de type float pm * et float pm *, et non pas float *. Pourquoi
ce choix, que va-t-il apporter aux performances du filtre?
• Reprendre votre code C, et utiliser le même type pour les coefficients du filtre.
Mesurer le nombre des cycles du traitement et comparer les résultats. Conclure.
• Refaire la partie qui concerne le filtrage en assembleur. Utiliser pour cela un buffer
circulaire (cf. cours). Tester le code et comparer.
Téléchargement