Jukebox Aléatoire

Porteur(s) du projet : Damien MUTI (Prof. de Numérique)

Date : 03/2021

objectif : Lancer des sons aléatoirement en appuyant sur un bouton.

Contexte : Sonnerie aléatoire

Fichiers :

Liens :

Capteurs/Actionneurs :

Bouton poussoir

Processing

Intentions : explication du projet et objectifs

Lorsqu'on appuie sur un bouton poussoir connecté à l'entrée digitale d'une carte Arduino, cette dernière envoie un message à Processing qui sélectionne aléatoirement une musique et la joue tant que le doigt reste appuyé sur le bouton.

Lorsqu'on appuie une une deuxième fois, le la même musique se lance tant qu'on tant que le doigt reste appuyé sur le bouton. En revanche, lorsqu'on appuie une troisième fois, une nouvelle musique est sélectionnée aléatoirement.

le schéma suivant résume la situation :

Plans et schémas de fonctionnement

Programme et montage Arduino

Le montage permettant de capter le signal provenant du bouton poussoir est le suivant :

Lorsqu'on appuie sur le bouton poussoir, la va valeur lue par la broche d'entrée digitale “pin” passe de 0V à 5V.

Le montage Arduino est le suivant :

Le programme permettant d'envoyer les données à Processing via le port série est le suivant : test_bouton_1.ino.zip

/*
Branchement sur ARduino NANO BLE
sur bread board - Branchement USB en bas
+ : 2 à droite
GND (-) : 14 à droite
ENtrée Bouton : 11 à gauche
*/
// constants won't change. They're used here to set pin numbers:
const int buttonPin = 2; // the number of the pushbutton pin
const int ledPin = 13; // the number of the LED pin
// variables will change:
int buttonState = 0; // variable for reading the pushbutton status
int inByte = 0; // incoming serial byte
void setup() {
// initialize the LED pin as an output:
pinMode(ledPin, OUTPUT);
// initialize the pushbutton pin as an input:
pinMode(buttonPin, INPUT);
// port Série
// start serial port at 9600 bps:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
// établir le contact avec Processing
establishContact(); // send a byte to establish contact until receiver responds
}
void loop() {
if (Serial.available() > 0) { // si un octet arrive sur le port série
//de la carte venant de Processing
// get incoming byte:
inByte = Serial.read();
// read the state of the pushbutton value:
buttonState = digitalRead(buttonPin);
// check if the pushbutton is pressed. If it is, the buttonState is HIGH.
// //et envoi de la valeur
if (buttonState == HIGH) { // bouton appuyé => 5V en D2 => envoi de 255 et allumer LED 13
//Serial.println(255);
Serial.write(255);
// turn LED on:
digitalWrite(ledPin, HIGH);
} else { // bouton relâché => 0V en D2 => envoi de 0 et éteindre LED 13
//Serial.println(0);
byte zero = 0;
Serial.write(zero);
// turn LED off:
digitalWrite(ledPin, LOW);
}
}
}
void establishContact() {
while (Serial.available() <= 0) {
Serial.print('A'); // send a capital A
delay(300);
}
}

Ce programme est inspiré de l'exemple SerialCallResponse.

Lorsque le bouton est relâché, la led est éteinte et la carte envoie 0 via le port série. Lorsque le bouton on allume la led et la carte envoie 255 via le port série.

Programme Processing

Les sons joués par Processing peuvent être mis dans le dossier “data” ou dans n'importe quel sous dossier contenu dans le dossier “data”. Le programme Processing va répertorier automatiquement tous les fichiers sonores avec l'extension :

mp3

aiff

wave

Dans sa boucle de fonctionnement, le programme Processing récupère les données venant de la carte Arduino, via le port série. Cette opération s'inspire du programme SerialCallResponse. Une fois les données récupérées, la méthode “gestionDuSon()” est appelée. l'algorithme de la gestion du son est le suivant :

jouer le son si le bouton est appuyé et si le son ne joue pas déjà :

tester le compteur de sonnerie. Si le compteur est égal à zéro, tirer un son aléatoire dans la liste des sons

lancer le son et incrémenter le compteur de sonnerie.

sinon (i.e. si si le bouton est relâché ET que le son joue) :

arrêter le son

tester si le compteur d'itérations de sonnerie est supérieur à interationSonnerie (= 2 ici), le remettre à 0 si c'est le cas.

Le programme Processing est le suivant : sonnerie_8_recherche_fichier.zip

sonnerie_8_recherche_fichier :

/** Affiche tactile interactive -
* Quand on appuie sur une touche (UP, DOWN, RIGHT, LEFT et espace ' '), cela lance une image, une vidéo, une annim, un son, etc...
*/
/// librairies
import processing.sound.*;
import processing.video.*;
import processing.serial.*;
// variables globales
SoundFile son; // un son - un seul lecteur CD audio
ArrayList <String> nomDesSons ;
java.util.List<String> extensions;
// bouton son actif ?
boolean son_actif = false;
float tempsDebutSon = 0; // temps du début de la musique a été joué
/// dialogue avec la carte Arduino
Serial myPort; // Create object from Serial class
boolean firstContact = false; // Whether we've heard from the microcontroller
int donneePortSerie; // entier converti de la chaine de caractère reçue sur le port série
// compteur sonnerie
int interationSonnerie = 2;
int compteurSonnerie = 0;
int indiceSon = 0;
// debug
boolean debug = false;
void setup() { // initialisation des paramètres d'affichage & chargement des sons, vidéos, etc.
size(500, 500);
noStroke();
background(0);
// chargement des nom des sons
String[] extensionsArray = {"wav", "aiff", "mp3"};
extensions = java.util.Arrays.asList(extensionsArray);
nomDesSons = rechercheTousLesSons();
son = new SoundFile(this, nomDesSons.get(0));
/// Port série
// Print a list of the serial ports, for debugging purposes:
printArray(Serial.list());
String portName = Serial.list()[0];
myPort = new Serial(this, portName, 9600);
}
void draw() {
fill(0);
rect(0, 0, width, height);
fill(255);
text(donneePortSerie, 100, 100);
}
void serialEvent(Serial myPort) {
// lire la donnée sur la port série
int inByte = myPort.read();
if (firstContact == false) {
if (inByte == 'A') {
myPort.clear(); // clear the serial port buffer
firstContact = true; // you've had first contact from the microcontroller
myPort.write('A'); // ask for more
println("communication avec la carte Arduino établie");
}
} else {
donneePortSerie = inByte; // lire la donnée et la stoquer dans inBuffer (chaine de caractères - String)
//debug
if (debug) {
println("bouton=" + donneePortSerie);
}
// tester la valeur du bouton et gérer le son
gestionSon();
// envoyer 'A' à la carte Arduino pour nouvelle lecture du bouton
myPort.write('A');
}
}

La partie “gestion du son” est la suivante :

//////////////////////////////////////////////// Son ///////////////////////////////////////////////////
void gestionSon(String nomDuSon) {
//if (donneePortSerie < seuil && donneePortSerie>0 && !son.isPlaying()) { // son
// jouer le son SSI si le buton est appuyé et si le son ne joue pas déjà
if (donneePortSerie == 255 && !son.isPlaying()) { // son
// lancement du son
lancerSon(nomDuSon);
} else if (donneePortSerie == 0 && son.isPlaying()) { //si le bouton est relâché ET que le son joue : arrêter le son
son.stop();
}
}
void gestionSon() { /// tirage aléatoire parmi la liste de son joué 2 fois
// jouer le son SSI si le bouton est appuyé et si le son ne joue pas déjà
if (donneePortSerie == 255 && !son.isPlaying()) { // son
// test du compteur de sonnerie
if (compteurSonnerie == 0){
//tirer un son aléatoire dans la liste des sons
indiceSon = floor(random(0, nomDesSons.size()));
}
// lancement du son
lancerSon(nomDesSons.get(indiceSon));
println(nomDesSons.get(indiceSon));
} else if (donneePortSerie == 0 && son.isPlaying()) { //si le bouton est relâché ET que le son joue : arrêter le son
son.stop();
// tester si le compteur d'itérations de sonnerie est supérieur à interationSonnerie (= 2 ici), le remettre à 0 si c'est le cas.
compteurSonnerie = compteurSonnerie % interationSonnerie;
}
}
void lancerSon(String nomDuSon) {
if (!son.isPlaying()) { // le son ne joue pas
// chargement du son 1
son = new SoundFile(this, nomDuSon);
// jouer le son
son.loop();
//incrémenter le compteur d'itérations de sonnerie
compteurSonnerie++;
}
}

Enfin, la lecture automatique des fichiers son est gérée par le code suivant :

ArrayList<String> rechercheTousLesSons() {
ArrayList<String> nomDesSons = new ArrayList<String>();
// Using just the path of this sketch to demonstrate,
// but you can list any directory you like.
String path = sketchPath();
// recherche de tous les dossiers et fichiers dans le dossier du sketch parent
ArrayList<File> allFiles = listFilesRecursive(path);
// recherche de tous les fichiers ".wav" et ".aiff"
if (allFiles != null) {
// recherche de tous les fichiers son : .mp3, .aif, .wav
for (File f : allFiles) {
String fname = f.getName();
String fileExt = fname.toLowerCase().substring(fname.lastIndexOf('.') + 1) ; // récupérer l'extension du fichier
if (extensions.contains(fileExt)) {
// fname.toLowerCase().endsWith(".wav") || fname.toLowerCase().endsWith(".aiff") || fname.toLowerCase().endsWith(".mp3")
nomDesSons.add(f.getAbsolutePath());
}
}
}
//println("\nnom de tous les fichiers son: ");
//printArray(nomDesSons.toArray());
//println("-----------------------");
return nomDesSons;
}
// Function to get a list of all files in a directory and all subdirectories
ArrayList<File> listFilesRecursive(String dir) {
ArrayList<File> fileList = new ArrayList<File>();
recurseDir(fileList, dir);
return fileList;
}
// Recursive function to traverse subdirectories
void recurseDir(ArrayList<File> a, String dir) {
File file = new File(dir);
if (file.isDirectory()) {
// If you want to include directories in the list
a.add(file);
File[] subfiles = file.listFiles();
for (int i = 0; i < subfiles.length; i++) {
// Call this function on all files in this directory
recurseDir(a, subfiles[i].getAbsolutePath());
}
} else {
a.add(file);
}
}