Nous avons essentiellement terminé notre projet, ayant complété la majorité de nos buts. L’application contient maintenant quatre tambours, dont une cymbale, un tom, un hi-hat et un snare, qui sont tous interchangeables et remplaçables. Il suffit simplement de se rendre dans le fichier data du fichier Airdrum et de changer les fichiers audio .mp3 ou de changer leurs noms. Cependant, on doit conserver les noms des fichiers audio existants, sinon on doit les changer à l’intérieur du code aussi. Nous avons trouvé que ce projet a testé nos connaissances de programmation. Bien sûr, sans le changement de plans nécessaire, il y aurait eu beaucoup plus de physique et de mathématiques à réaliser, mais nous avons fait notre possible pour en conserver le plus possible.
Voici une vidéo explicative sur notre projet:
Ci-dessous se retrouve le code écrit en Java contenu dans le logiciel Processing, si vous voulez simplement télécharger le projet au complet, rendez vous sur le site principal et cliquez sur le bouton pour télécharger ce dernier.
/* ATTENTION: Attendre deux secondes avec les
couleurs devant la webcam avant de frapper. */
/* Pour un meilleur fonctionnement, utiliser
une webcam sans balance des blancs automatique. */
import processing.video.*;
import processing.sound.*;
import lord_of_galaxy.timing_utils.*;
// Pour capturer la vidéo de la webcam
Capture video;
// Fichiers de sons
SoundFile snareFile;
SoundFile crashFile;
SoundFile tomFile;
SoundFile hihatFile;
// Couleur rouge et bleue
color trackColorRed;
color trackColorBlue;
float thresholdRed = 125; // changer ces valeurs pour trouver les meilleures
float thresholdBlue = 80; // selon votre caméra et votre éclairage
// Tambours (instruments)
boolean snareSound = true;
boolean crashSound = true;
boolean tomSound = true;
boolean hihatSound = true;
// Pour stocker les valeurs de Y pendant 1 seconde
ArrayList<Float> listAvgYRed = new ArrayList<Float>(59);
float frameRed = 0;
ArrayList<Float> listAvgYBlue = new ArrayList<Float>(59);
float frameBlue = 0;
// Pour le temps
boolean timeRestartRed = true;
Stopwatch timeRed = new Stopwatch(this);
boolean timeRestartBlue = true;
Stopwatch timeBlue = new Stopwatch(this);
// Vitesse à laquelle vous allez jouer
float playSpeed = 10;
void setup() {
// Détermine la taille de l'écran pour visionner la vidéo de la webcam
size(640, 480);
// Fait une liste des caméras, vous devez choisir la vôtre
String[] cameras = Capture.list();
// printArray(cameras);
video = new Capture(this, cameras[1]); // (choisissez l'indice de la caméra ici)
// Fichiers de sons
snareFile = new SoundFile(this, "snare.mp3");
crashFile = new SoundFile(this, "cymbal.mp3");
tomFile = new SoundFile(this, "tom.mp3");
hihatFile = new SoundFile(this, "hihat.mp3");
// Commence l'enregistrement de la webcam
video.start();
// Ce sont les couleurs à reconnaître (changez si vous avez d'autres couleurs)
trackColorRed = color(255,0,0); // en code RGB, donc 0 à 255
trackColorBlue = color(14, 138, 209); // exemple: jaune -> (255, 255, 0)
}
void captureEvent(Capture video)
{
// Permet de lire la vidéo
video.read();
}
void draw(){
// Pour transmettre la vidéo à l'écran
video.loadPixels();
// Pour la vidéo avec effet miroir
pushMatrix();
translate(width,0);
scale(-1,1);
image(video, 0, 0, width, height); // affiche la vidéo
popMatrix();
// Pour le rouge
float avgXRed = 0;
float avgYRed = 0;
int countRed = 0;
// Pour le snare
rect(540, 300, 100, 100);
// Pour le hi-hat
rect(420, 300, 100, 100);
// On trouve la concentration de rouge dans la vidéo
for (int x = 0; x < video.width; x++) {
for (int y = 0; y < video.height; y++){
int locRed = x + y*video.width;
color currentColor = video.pixels[locRed];
float r1 = red(currentColor);
float g1 = green(currentColor);
float b1 = blue(currentColor);
float rRed = red(trackColorRed);
float gRed = green(trackColorRed);
float bRed = blue(trackColorRed);
float dRed = dist(r1, g1, b1, rRed, gRed, bRed);
if (dRed < thresholdRed){
avgXRed += x;
avgYRed += y;
countRed++;
}
}
}
// Pour le bleu
float avgXBlue = 0;
float avgYBlue = 0;
int countBlue = 0;
// Rectangle de la cymbale
rect(0, 300, 100, 100);
// Rectangle du tom
rect(120, 300, 100, 100);
// On trouve la concentration de bleu dans la vidéo
for(int x = 0; x < video.width; x++) {
for(int y = 0; y < video.height; y++) {
int locBlue = x + y*video.width;
color currentColor = video.pixels[locBlue];
float r2 = red(currentColor);
float g2 = green(currentColor);
float b2 = blue(currentColor);
float rBlue = red(trackColorBlue);
float gBlue = green(trackColorBlue);
float bBlue = blue(trackColorBlue);
float dBlue = dist(r2, g2, b2, rBlue, gBlue, bBlue);
if (dBlue < thresholdBlue){
avgXBlue += x;
avgYBlue += y;
countBlue++;
}
}
}
// Pour le rouge
if (countRed > 0 )
{
// On positionne un cercle où la moyenne du rouge est situé dans l'écran
avgXRed = avgXRed / countRed;
avgYRed = avgYRed / countRed;
// On rempli les rectangles et le cercle de rouge
fill(trackColorRed);
strokeWeight (4.0);
stroke(0);
// Pour suivre le rouge avec x inversé
ellipse(-avgXRed +width,avgYRed,16,16);
// On ajoute la position à la liste de valeurs Y de rouge
listAvgYRed.add(avgYRed);
frameRed++;
// À chaque fois que le frame spécifié est enregistré
if (frameRed % playSpeed == 0 && timeRestartRed == true)
{
timeRed.start(); // on commence le timer
timeRestartRed = false;
}
// Si une frappe de snare est détectée
if(snareSound == true && avgYRed>=300 && avgYRed<=400 && avgXRed<=105)
{
// Calcule la vitesse de la frappe
float distance = 300 - listAvgYRed.get(56); // 0.06 sec avant la frappe
float temps = timeRed.time(); // temps écoulé depuis le 56ième frame
float vitesse = (distance/temps) * 100; /* fois 100 pour avoir des
plus gros ratios */
// Frappe plus forte
if (vitesse >= 10)
{
snareFile.amp(1);
}
// Frappe moins forte
if (vitesse < 10)
{
snareFile.amp(0.3);
}
// Joue le son du snare
snareFile.play();
snareSound = false;
// Permet de recommencer le timer
timeRestartRed = true;
}
// Après une frappe de snare
if(snareSound == false && avgYRed<=300)
{
snareSound = true; // permet de refrapper le snare
}
// Si une frappe de hihat est détectée
if(hihatSound == true && avgYRed>=300 && avgYRed<=400 && avgXRed<=225 && avgXRed>=115)
{
// Calcule la vitesse de la frappe
float distance = 300 - listAvgYRed.get(56); // 0.06 sec avant la frappe
float temps = timeRed.time(); // temps écoulé depuis le 56ième frame
float vitesse = (distance/temps) * 100; /* fois 100 pour avoir des
plus gros ratios */
// Frappe plus forte
if (vitesse >= 10)
{
hihatFile.amp(0.6);
}
// Frappe moins forte
if (vitesse < 10)
{
hihatFile.amp(0.1);
}
// Joue le son du hi-hat
hihatFile.play();
hihatSound = false;
// Permet de recommencer le timer
timeRestartRed = true;
}
// Après une frappe de hihat
if (hihatSound == false && avgYRed<=300)
{
hihatSound = true; // permet de refrapper le hihat
}
}
// Si la liste de valeurs du Y du rouge est plein
if (listAvgYRed.size() >= 59)
{
listAvgYRed.remove(0); // on enlève le premier élément
}
// Pour le bleu
if (countBlue > 0)
{
// On positionne un cercle où la moyenne du bleu est situé dans l'écran
avgXBlue = avgXBlue / countBlue;
avgYBlue = avgYBlue / countBlue;
// On rempli les rectangles et le cercle de bleu
fill(trackColorBlue);
strokeWeight(4.0);
stroke(0);
// Pour suivre le bleu avec x inversé
ellipse(-avgXBlue + width, avgYBlue, 16, 16);
// On ajoute la position à la liste de valeurs Y de bleu
listAvgYBlue.add(avgYBlue);
frameBlue++;
// À chaque fois que le frame spécifié est enregistré
if (frameBlue % playSpeed == 0 && timeRestartBlue == true)
{
timeBlue.start(); // on commence le timer
timeRestartBlue = false;
}
// Si une frappe de cymbale est détectée
if (crashSound == true && avgYBlue>=300 && avgYBlue<=400 && avgXBlue>=535)
{
// Calcule la vitesse de la frappe
float distance = 300 - listAvgYBlue.get(56); // 0.06 sec avant la frappe
float temps = timeBlue.time(); // temps écoulé depuis le 56ième frame
float vitesse = (distance/temps) * 100; /* fois 100 pour avoir des
plus gros ratios */
// Frappe plus forte
if (vitesse >= 10)
{
crashFile.amp(0.8);
}
// Frappe moins forte
if (vitesse < 10)
{
crashFile.amp(0.2);
}
crashFile.play(); // on joue le son du crash (cymbale)
crashSound = false;
// Permet de recommencer le timer
timeRestartBlue = true;
}
// Après la frappe de cymbale
if (crashSound == false && avgYBlue<=300)
{
crashSound = true; // permet de refrapper la cymbale
}
// Si une frappe de tom est détectée
if (tomSound == true && avgYBlue>=300 && avgYBlue<=400 && avgXBlue<=525 && avgXBlue>=415)
{
// Calcule la vitesse de la frappe
float distance = 300 - listAvgYBlue.get(56); // 0.06 sec avant la frappe
float temps = timeBlue.time(); // temps écoulé depuis le 56ième frame
float vitesse = (distance/temps) * 100; /* fois 100 pour avoir des
plus gros ratios */
// Frappe plus forte
if (vitesse >= 10)
{
tomFile.amp(0.8);
}
// Frappe moins forte
if (vitesse < 10)
{
tomFile.amp(0.2);
}
tomFile.play(); // on joue le son du tom
tomSound = false;
// Permet de recommencer le timer
timeRestartBlue = true;
}
// Après la frappe de tom
if (tomSound == false && avgYBlue<=300)
{
tomSound = true; // permet de refrapper le tom
}
}
// Si la liste de valeurs du Y du rouge est plein
if (listAvgYBlue.size() >= 59)
{
listAvgYBlue.remove(0); // on enlève le premier élément
}
}