/* @pjs preload="PartitionLune.png"; */ void calcScore() { score = 0; float tmpScore = 0; for (int i=0; i<=numnote; i++) // Pour chaque note cliquée { for (int j=0; j < tmpNotes.length; j++) // La comparer à chaque note de la partition { if (notePressed[i][0] != tmpNotes[j][0]) // Si la note est différente continue; // Passer à la note suivante if(i == 0 && j == 0) // Si c'est la toute première note tmpScore = 1; // Donner d'office un score de 1000 if(i == 0 || j == 0) // Si on est à la première note d'un des deux tableaux continue; // Passer à la note suivante float deltaTime = sqrt(abs((notePressed[i][1]-notePressed[i-1][1]) - (tmpNotes[j][1]-tmpNotes[j-1][1]))); // Calcul de la différence de durée de la note de la partition et de la note cliquée passé à la racine carrée pour donner un résultat plus adapté if (deltaTime == 0) // Si les notes ont exactement la même durée tmpScore = 1; // Donner un score de 1000 else if (1/((float)deltaTime) > tmpScore) // Si le score est le plus élevé pour une note cliquée comparée à plusieurs note de la partition tmpScore = 1/((float)deltaTime); // Enregistrer ce score (l'inverse, puisque que plus la différence de temps est grande, moins le score est important) } score += tmpScore*1000; tmpScore = 0; } } void setup() { PartitionLune = loadImage(imagePartition); // Charge l'image de la partition initSynth(); defineKeys(); //size(windowSize[0], windowSize[1]); size(1130,650); background(255); //PFont SuperPoliceDuPoneyQuiTousse = loadFont(font); // Importation de la police. //textFont(SuperPoliceDuPoneyQuiTousse, tailleTexte); // Appel de la police et de sa taille. PFont myFont = createFont("Boingo", 32); textFont(myFont); drawKeyboard(); drawDisplay(); dimage(PartitionLune, imageCoordinates[0], imageCoordinates[1], imageSize[0], imageSize[1]); } void draw() { if (events[MOUSEPRESSED] || events[KEYPRESSED]) // Si un clic a eu lieu, mettre à jour { playNotes(); drawKeyboard(); drawDisplay(); } else if (events[MUSICSTARTED]) // Ou si la musique est lancée { if (events[MUSICFINISHED]) { events[MUSICSTARTED] = false; events[MUSICFINISHED] = false; drawDisplay(); } else { playNotes(); drawKeyboard(); } } if (events[RECORDINGSTARTED]) { if (events[RECORDINGSTOPPED]) { events[RECORDINGSTARTED] = false; notePressed[0][1] = 0; calcScore(); numnote = 0; drawDisplay(); } } events[MOUSEPRESSED] = false; events[KEYPRESSED] = false; events[PLAYPRESSED] = false; } int whiteKeys() // Détection du clic sur une touche blanche { int activatedKey = 0; for (int j=0; j= xPos*windowSize[0]) && (mouseX <= (xPos+(keyWidth[1]/(numOctaves)))*windowSize[0]) && (mouseY <= (keyboardYCoordinate+keyHeight[1])*windowSize[1])) activatedKey = (j*7)+(i+1); } } return activatedKey; } int blackKeys() // Détection du clic sur une touche noire { int activatedKey = 0; for (int j=0; j= xPos*windowSize[0]) && (mouseX <= (xPos+(keyWidth[0]/(numOctaves)))*windowSize[0]) && (mouseY <= (keyboardYCoordinate+keyHeight[0])*windowSize[1])) activatedKey = (j*5)+(i+1); } } return activatedKey; } void mousePressed() { events[MOUSEPRESSED] = true; if (mouseY >= keyboardYCoordinate*windowSize[1]) // Si le clic est effectué au niveau du clavier { int activatedKey = blackKeys(); if (activatedKey == 0) { activatedKey = whiteKeys(); if (activatedKey == 0) return; processNote(activatedKey, true, true, false); } else processNote(activatedKey, false, true, false); } else { if (!events[RECORDINGSTARTED] && sqrt(pow((playButtonCoordinates[0]*windowSize[0] - (float)mouseX), 2) + pow((playButtonCoordinates[1]*windowSize[1] - (float)mouseY), 2)) <= buttonRadius*windowSize[0]) // Si une touche d'un chiffre est cliquée (distance entre le centre du cercle et l'endroit cliqué inférieur au rayon) events[PLAYPRESSED] = true; if (!events[MUSICSTARTED] && sqrt(pow((recordButtonCoordinates[0]*windowSize[0] - (float)mouseX), 2) + pow((recordButtonCoordinates[1]*windowSize[1] - (float)mouseY), 2)) <= buttonRadius*windowSize[0]) // Si une touche d'un chiffre est cliquée (distance entre le centre du cercle et l'endroit cliqué inférieur au rayon) { if (events[RECORDINGSTARTED]) events[RECORDINGSTOPPED] = true; else if (events[RECORDINGSTOPPED]) { events[RECORDINGSTARTED] = true; events[RECORDINGSTOPPED] = false; score = -1; } else events[RECORDINGSTARTED] = true; } } } void mouseReleased() { events[MOUSEPRESSED] = true; if (mouseY >= keyboardYCoordinate*windowSize[1]) // Si le clic est effectué au niveau du clavier { int activatedKey = blackKeys(); if (activatedKey == 0) { activatedKey = whiteKeys(); if (activatedKey == 0) return; processNote(activatedKey, true, false, false); } else processNote(activatedKey, false, false, false); } } void keyPressed() { events[KEYPRESSED] = true; if (keyCode < 523) { int activatedKey = touchesNoires[keyCode]; if (activatedKey == 0) { activatedKey = touchesBlanches[keyCode]; if (activatedKey == 0) return; processNote(activatedKey, true, true, false); } else processNote(activatedKey, false, true, false); } } void keyReleased() { events[KEYPRESSED] = true; if (keyCode < 523) { int activatedKey = touchesNoires[keyCode]; if (activatedKey == 0) { activatedKey = touchesBlanches[keyCode]; if (activatedKey == 0) return; processNote(activatedKey, true, false, false); } else processNote(activatedKey, false, false, false); } } /* Configuration de la partition */ int[][] notes = { /* Première ligne */ { 8, 1, 1 } , { 8, 1, 1 } , { 8, 1, 1 } , { 9, 1, 1 } , { 10, 1, 2 } , { 9, 1, 2 } , { 8, 1, 1 } , { 10, 1, 1 } , { 9, 1, 1 } , { 9, 1, 1 } , { 8, 1, 4 }, /* Deuxième ligne */ { 8, 1, 1 } , { 8, 1, 1 } , { 8, 1, 1 } , { 9, 1, 1 } , { 10, 1, 2 } , { 9, 1, 2 } , { 8, 1, 1 } , { 10, 1, 1 } , { 9, 1, 1 } , { 9, 1, 1 } , { 8, 1, 4 }, /* Troisième ligne */ { 9, 1, 1 } , { 9, 1, 1 } , { 9, 1, 1 } , { 9, 1, 1 } , { 6, 1, 2 } , { 6, 1, 2 } , { 9, 1, 1 } , { 8, 1, 1 } , { 7, 1, 1 } , { 6, 1, 1 } , { 5, 1, 4 }, /* Quatrième ligne */ { 8, 1, 1 } , { 8, 1, 1 } , { 8, 1, 1 } , { 9, 1, 1 } , { 10, 1, 2 } , { 9, 1, 2 } , { 8, 1, 1 } , { 10, 1, 1 } , { 9, 1, 1 } , { 9, 1, 1 } , { 8, 1, 4 } }; // Tableau de tableaux d'entiers pour les notes // notes[numnote][0] est la note à jouer, notes[numnote][1] est la durée de la note int bpm = 115; // tempo int timer = 0; // Initialisation du chronomètre à 0 int numnote = 0; // La première note à jouer a l'indice 0 String imagePartition = "PartitionLune.png"; /* Configuraton du synthétiseur */ int midiChannelNumber = 1; // Monotimbral, utilisation du 1er canal uniquement. int midiVelocity = 127; // Force de la note, de 0 à 127 int baseNote = 72; // Note de base à partir de laquelle les autres seront calculées. 72 : Do4 MidiChannel channel; /* Configuration de l'affichage */ float buttonRadius = 0.025; // Rayon du cercle des boutons //String font = "CurlzMT-48.vlw"; // Fichier de la police String font = "Boingo.ttf"; // Fichier de la police int tailleTexte = 30; int[] windowSize = {1130, 650}; // Largeur et hauteur de la fenêtre float[] keyWidth = {0.070, 0.144}; // Largeur touche noire et touche blanche float[] keyHeight = {0.358, 0.538}; // Hauteur touche noire et touche blanche float[] imageSize = {0.633, 0.461}; // Largeur et hauteur de la partition float[] imageCoordinates = {0.183, 0}; // Coordonnées x et y de la partition float[] keyXCoordinates = {0.096, 0.262, 0.518, 0.678, 0.838}; // Placement horizontal des touches noires sur une octave float keyboardYCoordinate = 0.462; // Placement vertical du clavier int numOctaves = 2; // Nombre d'octave à afficher String[] nomTouchesBlanches = {"Tab", "A", "Z", "E", "R", "T", "Y", "U", "I", "O", "P", "^", "$", "Enter"}; String[] nomTouchesNoires = {"&", "é", "'", "(", "-", "_", "ç", ")", "=", "<-"}; float[] playButtonCoordinates = {0.90, 0.10}; float[] recordButtonCoordinates = {0.90, 0.20}; /* Déclaration et initialisation de diverses constantes et variables */ /* Gestion des évènements */ final int MOUSEPRESSED = 0; final int KEYPRESSED = 1; final int PLAYPRESSED = 2; final int MUSICSTARTED = 3; final int MUSICFINISHED = 4; final int RECORDINGSTARTED = 5; final int RECORDINGSTOPPED = 6; final int MUSICPLAYED = 7; boolean[] events = new boolean[8]; /* Stockages des touches cliquées */ int[][] notePressed = new int[512][2]; // Un maximum de 512 notes peuvent être enregistrées int[][] keysPressed = {new int[numOctaves*5], new int[numOctaves*7]}; // Tableau des touches cliquées. keysPressed[0] pour les touches noires et keysPressed[1] pour les blanches int[][] tmpNotes = new int[notes.length][2]; // Stockage du timestamp des notes de la partition int score = -1; PImage PartitionLune; int[] touchesNoires = new int[523]; int[] touchesBlanches = new int[523]; /* Associations des touches du clavier aux notes */ void defineKeys() { // Touches blanches touchesBlanches[9] = 1; touchesBlanches[65] = 2; touchesBlanches[90] = 3; touchesBlanches[69] = 4; touchesBlanches[82] = 5; touchesBlanches[84] = 6; touchesBlanches[89] = 7; touchesBlanches[85] = 8; touchesBlanches[73] = 9; touchesBlanches[79] = 10; touchesBlanches[80] = 11; touchesBlanches[130] = 12; touchesBlanches[514] = 12; touchesBlanches[160] = 12; touchesBlanches[515] = 13; touchesBlanches[164] = 13; touchesBlanches[10] = 14; // Touches noires touchesNoires[49] = 1; touchesNoires[50] = 2; touchesNoires[52] = 3; touchesNoires[53] = 4; touchesNoires[54] = 5; touchesNoires[56] = 6; touchesNoires[57] = 7; touchesNoires[522] = 8; touchesNoires[169] = 8; touchesNoires[61] = 9; touchesNoires[8] = 10; } /* Redéfinition de fonctions pour transformer les coordonnées relatives en coordonnées absolues */ void drect(float i, float j, float k, float l) { rect(i*windowSize[0], j*windowSize[1], k*windowSize[0], l*windowSize[1]); } void dtriangle(float i, float j, float k, float l, float m, float n) { triangle(i*windowSize[0], j*windowSize[1], k*windowSize[0], l*windowSize[1], m*windowSize[0], n*windowSize[1]); } void dellipse(float i, float j, float k, float l) { ellipse(i*windowSize[0], j*windowSize[1], k*windowSize[0], l*windowSize[0]); } void dtext(String t, float x, float y) { text(t, x*windowSize[0], y*windowSize[1]); } void dimage(PImage p, float i, float j, float k, float l) { image(p, i*windowSize[0], j*windowSize[1], k*windowSize[0], l*windowSize[1]); } /* Fin de redéfinition des fonctions */ void drawKeyboard() { stroke(0); // Dessine chaque octaves for (int j=0; j 0) return -1; if (white) { keyNumber = (activatedKey-1)%7; octave = (activatedKey-1-keyNumber)/7; if (keyNumber < 3) keyNumber *= 2; else keyNumber = (keyNumber*2)-1; } else { keyNumber = (activatedKey-1)%5; octave = (activatedKey-1-keyNumber)/5; if (keyNumber < 2) keyNumber = (keyNumber*2)+1; else keyNumber = (keyNumber*2)+2; } octave -= (numOctaves-numOctaves%2)/2; keyNumber = octave*12+baseNote+keyNumber; if(on) { MIDI.noteOn(0, keyNumber, midiVelocity, 0); keysPressed[white ? 1 : 0][activatedKey-1] = 1; } else { MIDI.noteOff(0, keyNumber, 0); keysPressed[white ? 1 : 0][activatedKey-1] = 0; } if (on && events[RECORDINGSTARTED]) { if(numnote >= notePressed.length) events[RECORDINGSTOPPED] = true; else { notePressed[numnote][0] = keyNumber; if (numnote != 0) notePressed[numnote][1] = millis() - notePressed[0][1]; else notePressed[numnote][1] = millis(); numnote++; } } return keyNumber; } void playNotes() // Joue les notes de notes[][], appelé lors du clic sur le bouton lecture et jusqu'à la fin du tableau { if (timer == 0) // Si le chronomètre n'est pas lancé { if (events[PLAYPRESSED]) // Et que la touche lecture est cliquée { timer = millis(); // Lancer le chronomètre events[MUSICSTARTED] = true; if(!events[MUSICPLAYED]) events[MUSICPLAYED] = true; } } else if (numnote < notes.length) // Si la note suivante existe { if ( numnote == 0 || timer + notes[numnote-1][2]*(60000/bpm) < millis() ) // et que c'est la première note ou la durée de la note précédente est écoulée { if(numnote > 0) processNote(notes[numnote-1][0], notes[numnote-1][1]%2!=0, false, true); int keyNumber = processNote(notes[numnote][0], notes[numnote][1]%2!=0, true, true); /* Remplit un tableau temporaire comportant le timestamp de chaque note. */ tmpNotes[numnote][0] = keyNumber; if (numnote != 0) tmpNotes[numnote][1] = millis()-tmpNotes[0][1]; else tmpNotes[0][1] = millis(); //channel.noteOn(notes[numnote][0], midiVelocity); // Jouer la note numnote++; // Passer à la note suivante timer = millis(); // Réinitialiser le chronomètre } } else // Si la lecture est terminée { if ( numnote == 0 || timer + notes[numnote-1][2]*(60000/bpm) < millis() ) // et que c'est la première note ou la durée de la note précédente est écoulée { processNote(notes[numnote-1][0], notes[numnote-1][1]%2!=0, false, true); tmpNotes[0][1] = 0; // Le timestamp de la première note est 0. timer = 0; numnote = 0; events[MUSICFINISHED] = true; } } }