diff options
Diffstat (limited to 'Processing')
-rw-r--r-- | Processing/Synthe3000/Config.pde | 231 | ||||
-rw-r--r-- | Processing/Synthe3000/Draw.pde | 86 | ||||
-rw-r--r-- | Processing/Synthe3000/Synth.pde | 114 | ||||
-rw-r--r-- | Processing/Synthe3000/Synthe3000.pde | 200 | ||||
-rw-r--r-- | Processing/Synthe3000/data/CurlzMT-48.vlw | bin | 0 -> 146194 bytes | |||
-rw-r--r-- | Processing/Synthe3000/data/PartitionLune.png | bin | 0 -> 28258 bytes | |||
-rw-r--r-- | Processing/Synthe3000/sketch.properties | 2 |
7 files changed, 633 insertions, 0 deletions
diff --git a/Processing/Synthe3000/Config.pde b/Processing/Synthe3000/Config.pde new file mode 100644 index 0000000..c3638b3 --- /dev/null +++ b/Processing/Synthe3000/Config.pde @@ -0,0 +1,231 @@ +/* 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 +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; +} diff --git a/Processing/Synthe3000/Draw.pde b/Processing/Synthe3000/Draw.pde new file mode 100644 index 0000000..7b28d50 --- /dev/null +++ b/Processing/Synthe3000/Draw.pde @@ -0,0 +1,86 @@ +/* Redéfinition de fonctions pour transformer les coordonnées relatives en coordonnées absolues */ +void rect(float i, float j, float k, float l) +{ + super.rect(i*windowSize[0], j*windowSize[1], k*windowSize[0], l*windowSize[1]); +} + +void triangle(float i, float j, float k, float l, float m, float n) +{ + super.triangle(i*windowSize[0], j*windowSize[1], k*windowSize[0], l*windowSize[1], m*windowSize[0], n*windowSize[1]); +} + +void ellipse(float i, float j, float k, float l) +{ + super.ellipse(i*windowSize[0], j*windowSize[1], k*windowSize[0], l*windowSize[0]); +} + +void text(String t, float x, float y) +{ + super.text(t, x*windowSize[0], y*windowSize[1]); +} + +void image(PImage p, float i, float j, float k, float l) +{ + super.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<numOctaves; j++) { + fill(255); + // Dessine les touches blanches + for (int i=0; i<7; i++) { + if (keysPressed[1][(j*7)+i] != 0) fill(0, 255, 0); // Colorer la touche en vert si elle est appuyée + rect((keyWidth[1]/numOctaves)*i+(float)j/numOctaves, keyboardYCoordinate, keyWidth[1]/(numOctaves), keyHeight[1]); // Rectangle touche blanche + fill(255,0,255); + text(nomTouchesBlanches[i+(j*7)], 0.01+(keyWidth[1]/numOctaves)*i+(float)j/numOctaves, 0.962); // Affiche le caractère de la touche + fill(255); + } + fill(0); + // Dessine les touches noires + for (int i=0; i<keyXCoordinates.length; i++) { + if (keysPressed[0][(j*5)+i] != 0) fill(255, 255, 0); // Colorer la touche en jaune si elle est appuyée + rect((keyXCoordinates[i]/numOctaves)+(float)j/numOctaves, keyboardYCoordinate, keyWidth[0]/(numOctaves), keyHeight[0]); // Rectangle touche noire + fill(255,0,255); + text(nomTouchesNoires[i+(j*5)], 0.01+(keyXCoordinates[i]/numOctaves)+(float)j/numOctaves, keyboardYCoordinate+0.32); // Affiche le caractère de la touche + fill(0); + } + } +} + +void drawDisplay() // Dessine l'affichage, appelé lors du lancement et après chaque clic. +{ + stroke(0); + + /* Bouton lecture */ + if (events[MUSICSTARTED]) fill(0, 0, 255); // Colorisation du bouton lecture si activé + else fill(255); + ellipse(playButtonCoordinates[0], playButtonCoordinates[1], buttonRadius*2, buttonRadius*2); // Contour + fill(0); + triangle(playButtonCoordinates[0]-0.01, playButtonCoordinates[1]-0.02, playButtonCoordinates[0]+0.02, playButtonCoordinates[1], playButtonCoordinates[0]-0.01, playButtonCoordinates[1]+0.02); // Logo lecture + + /* Bouton enregistrer */ + if(events[MUSICPLAYED]) + { + if (events[RECORDINGSTARTED]) fill(255, 0, 0); // Colorisation du bouton enregistrer si activé + else fill(255); + ellipse(recordButtonCoordinates[0], recordButtonCoordinates[1], buttonRadius*2, buttonRadius*2); // Contour + fill(0); + ellipse(recordButtonCoordinates[0], recordButtonCoordinates[1], buttonRadius, buttonRadius); // Logo + } + + if (!events[RECORDINGSTARTED] && events[RECORDINGSTOPPED] && score != -1) + { + fill(255,0,255); + text("Score :" + Integer.toString(score), 0.86, 0.32); // Affichage du score + } + else + { + fill(255); + stroke(255); + rect(0.85, 0.25, 0.15, 0.10); + } +} diff --git a/Processing/Synthe3000/Synth.pde b/Processing/Synthe3000/Synth.pde new file mode 100644 index 0000000..ec310bb --- /dev/null +++ b/Processing/Synthe3000/Synth.pde @@ -0,0 +1,114 @@ +void initSynth() // Initialisation du synthétiseur +{ + Synthesizer synth = null; + + try { synth = MidiSystem.getSynthesizer(); } + catch (MidiUnavailableException e) { exit(); } + + try { synth.open(); } + catch (MidiUnavailableException e) { exit(); } + + channel = synth.getChannels()[midiChannelNumber]; +} + +int processNote(int activatedKey, boolean white, boolean on, boolean autoplay) // Détection de la note cliquée (activatedKey : code de la touche, white : touche blanche ou touche noire, on : activer ou désactiver, autoplay : jouée par l'utilisateur ou par l'ordinateur) +{ + int keyNumber; + int octave; + + if(autoplay) + { + if(on) + keysPressed[white ? 1 : 0][activatedKey-1] = -1; + else + keysPressed[white ? 1 : 0][activatedKey-1] = 0; + } + else if(on && keysPressed[white ? 1 : 0][activatedKey-1] > 0) // Si déjà activée + return -1; + + if (white) + { + keyNumber = (activatedKey-1)%7; // Calcul du numéro de touche dans l'octave + octave = (activatedKey-1-keyNumber)/7; // Calcul de l'octave + 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; // Ajustement de l'octave + keyNumber = octave*12+baseNote+keyNumber; // Calcul du code MIDI de la note à jouer + if(on) + { + channel.noteOn(keyNumber, midiVelocity); // Joue la note + keysPressed[white ? 1 : 0][activatedKey-1] = 1; // La place dans le tableau des notes activées pour la colorer et ne pas la rejouer + } + else + { + channel.noteOff(keyNumber, midiVelocity); + keysPressed[white ? 1 : 0][activatedKey-1] = 0; + } + + if (on && events[RECORDINGSTARTED]) + { + if(numnote >= notePressed.length) // Arrête l'enregistrement si le nombre de note dépasse la longueur du tableau + events[RECORDINGSTOPPED] = true; + else + { + notePressed[numnote][0] = keyNumber; // Enregistre le code de la note + if (numnote != 0) notePressed[numnote][1] = millis() - notePressed[0][1]; // Enregistre la durée de la note + 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; + } + } +} diff --git a/Processing/Synthe3000/Synthe3000.pde b/Processing/Synthe3000/Synthe3000.pde new file mode 100644 index 0000000..1167a40 --- /dev/null +++ b/Processing/Synthe3000/Synthe3000.pde @@ -0,0 +1,200 @@ +import javax.sound.midi.MidiSystem; +import javax.sound.midi.Synthesizer; +import javax.sound.midi.MidiChannel; +import javax.sound.midi.MidiUnavailableException; + +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]); + background(255); + PFont SuperPoliceDuPoneyQuiTousse = loadFont(font); // Importation de la police. + textFont(SuperPoliceDuPoneyQuiTousse, tailleTexte); // Appel de la police et de sa taille. + drawKeyboard(); + drawDisplay(); + image(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; // La première note commence à 0ms + 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<numOctaves; j++) { // Pour chaque octave + for (int i=0; i<7; i++) { // Pour chaque touche blanche + float xPos = (keyWidth[1]/numOctaves)*i+(float)j/numOctaves; // Abscisse relative de la touche + if ((mouseX >= xPos*windowSize[0]) && (mouseX <= (xPos+(keyWidth[1]/(numOctaves)))*windowSize[0]) && (mouseY <= (keyboardYCoordinate+keyHeight[1])*windowSize[1])) + activatedKey = (j*7)+(i+1); // Numéro de la touche cliquée + } + } + return activatedKey; +} + +int blackKeys() // Détection du clic sur une touche noire +{ + int activatedKey = 0; + for (int j=0; j<numOctaves; j++) { // Pour chaque octave + for (int i=0; i<keyXCoordinates.length; i++) { // Pour chaque touche noire + float xPos = (keyXCoordinates[i]/numOctaves)+(float)j/numOctaves; // Abscisse relative de la touche + if ((mouseX >= xPos*windowSize[0]) && (mouseX <= (xPos+(keyWidth[0]/(numOctaves)))*windowSize[0]) && (mouseY <= (keyboardYCoordinate+keyHeight[0])*windowSize[1])) + activatedKey = (j*5)+(i+1); // Numéro de la touche cliquée + } + } + return activatedKey; +} + +void mousePressed() { + events[MOUSEPRESSED] = true; + + if (mouseY >= keyboardYCoordinate*windowSize[1]) // Si le clic est effectué au niveau du clavier + { + int activatedKey = blackKeys(); // Numéro de la touche si touche noire cliquée, sinon 0 + + if (activatedKey == 0) // Si ce n'est pas une touche noire + { + activatedKey = whiteKeys(); // Numéro de la touche si touche blanche cliquée, sinon 0 + if (activatedKey == 0) // Aucune touche cliquée, ne devrait pas survenir ici + 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 la touche lecture 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 la touche enregistrer 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() // Pareil que mousePressed() mais lorsque la touche est relachée +{ + events[MOUSEPRESSED] = true; + + if (mouseY >= keyboardYCoordinate*windowSize[1]) + { + 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() { // Pareil que mousePressed() mais pour le clavier + events[KEYPRESSED] = true; + + if (keyCode < 523) // Pas de touche associée à un code supérieur à 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() // Pareil que keyPressed() mais lorsque la touche est relachée +{ + 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); + } +} + diff --git a/Processing/Synthe3000/data/CurlzMT-48.vlw b/Processing/Synthe3000/data/CurlzMT-48.vlw Binary files differnew file mode 100644 index 0000000..48e2bdf --- /dev/null +++ b/Processing/Synthe3000/data/CurlzMT-48.vlw diff --git a/Processing/Synthe3000/data/PartitionLune.png b/Processing/Synthe3000/data/PartitionLune.png Binary files differnew file mode 100644 index 0000000..e5e5cfe --- /dev/null +++ b/Processing/Synthe3000/data/PartitionLune.png diff --git a/Processing/Synthe3000/sketch.properties b/Processing/Synthe3000/sketch.properties new file mode 100644 index 0000000..8630fa2 --- /dev/null +++ b/Processing/Synthe3000/sketch.properties @@ -0,0 +1,2 @@ +mode.id=processing.mode.java.JavaMode +mode=Java |