Je ne sais pas si mon dernier billet sur la façon de produire du son par programmation C++ vous a impressionné, mais quand on commence dans quelque chose, il faut partir par la base pour bien comprendre ce qu'on fait. Alors je poursuis l'exemple en poussant un peu plus loin les notions sans toutefois aller jusqu'à la lecture de fichiers musicaux (ça viendra).
Le programme initial comportait une limitation importante : celle de jouer des sons uniquement au même octave (j'avais choisi le 4ème). Pour ma démonstration, j'ai apporté quelques modifications en calculant la fréquence des notes de la gamme majeure à chaque octave, du 4ème au 7ème (les plus jolis et perceptibles à l'oreille). Si vous souhaitez tester à des octaves plus hauts, remarquez que plus la fréquence est élevée, plus le volume du son baisse. L'oreille humaine est sensible à des sons compris entre 20 et 20000 Hz (expérimentez le avec ce programme).
Pour commencer, j'inclus 2 librairies supplémentaires :
- math.h pour pouvoir utiliser la fonction pow (calcul de puissance)
- stdio.h pour utiliser printf et imprimer à l'écran la fréquence de chaque note calculée pour suivre la trace (optionnel)
Dans la fonction principale (main), je déclare les différentes variables utilisées au cours du programme. Ceux qui ne sont pas familiers avec la programmation se demanderont peut-être ce que représente notes[]. C'est un tableau qui contient 7 éléments (numérotés dans son fonctionnement interne de 0 à 6). J'y place les notes de base dans l'ordre, ce qui me permettra de parcourir la liste un élément à la fois.
Je lance ensuite 2 boucles imbriquées (for). La première indique que pour chaque octave, je fais aussi une itération sur chaque note du tableau en calculant sa fréquence et en l'envoyant vers le haut-parleur système.
#include <windows.h>Une autre notion qui peut être nouvelle pour vous est l'appel à printf qui permet d'envoyer du texte dans le terminal. Sa syntaxe particulière permet le formatage des données, dans ce cas-ci %f indique que la variable passée comme deuxième paramètre sera de type float (nombre à virgule flottante). Il est suivi d'un \n pour faire un saut de ligne à l'écran. On peut visualiser les notes au même moment qu'elles jouent dans le terminal mais la fenêtre se fermera toute seule si on ne fait pas l'appel à system("PAUSE") à la fin. Ceci permettra d'interrompre le programme et d'afficher la phrase "Press any key to continue...". Comme Homer Simpsons, essayez de figurer quelle touche correspond à "Any"...
#include <math.h>
#include <stdio.h>
using namespace std;
/* Définition des contantes à l'octave 0 et de chaque fréquence */
#define C0 16.35 // DO
#define D0 18.35 // RE
#define E0 20.60 // MI
#define F0 21.83 // FA
#define G0 24.50 // SOL
#define A0 27.50 // LA
#define B0 30.87 // SI
#define RATIO_OCTAVE 2
int main()
{
float notes[] = {C0, D0, E0, F0, G0, A0, B0};
float current_note;
float frequency;
float freq_octave;
int idx;
int octave;
for(octave=1 ; octave<=7 ; octave++){
freq_octave = pow(RATIO_OCTAVE, octave);
for(idx=0 ; idx<7 ; idx++){
current_note = notes[idx];
frequency = current_note * freq_octave;
printf("%f\n", frequency);
Beep( (int)frequency, 250);
}
}
system("PAUSE");
return EXIT_SUCCESS;
}
Enfin, c'est possible que vous rencontriez l'erreur suivante au moment de la compilation, si vous omettez un léger détail :
Beep(current_note * freq_octave, 250);
[Warning] passing float for converting 1 of BOOL Beep(DWORD, DWORD)
En effet, la fonction Beep() prend comme paramètre un nombre entier (integer en anglais) qui correspond à la fréquence ainsi qu'un deuxième paramètre pour la durée en millisecondes. Or, nous avons calculé la fréquence et l'avons stocké dans une variable de type float, ce qui est incompatible. Pour corriger le tout, il sera nécessaire d'arrondir la valeur de la variable en convertissant son type de float à integer en utilisant un mécanisme qu'on appelle "casting". Devant la variable frequency, on ajoutera entre parenthèse le type dans lequel on veut caster : (int).
En résumé, si vous êtes nouvellement initié à la programmation C++, vous avez déjà vu plusieurs concepts par la pratique :
- inclusion de librairies pour utiliser des fonctions spécialisées
- déclaration de constantes, de différents types de variables dont les tableaux (listes)
- le code placé entre /* et */ n'est pas compilé et exécuté, ce qui permet de mettre des commentaires pour documenter le programme
- boucles for imbriquées
- impression à l'écran en mode terminal
- faire sortir du son système