VII. Interagir▲
VII-A. Les événements clavier▲
Capter les actions de l'utilisateur sur le clavier de votre ordinateur (les événements clavier) et s'en servir pour faire réagir votre programme constitue une première forme d'interactivité. Nous allons découvrir dans ce chapitre comment récupérer les informations liées au clavier en fabriquant une machine à écrire très simple qui nous permettra d'imprimer du texte sur l'écran.
VII-A-1. Actions▲
La valeur de la dernière touche appuyée est stockée dans la variable key proposée par défaut dans Processing. Cette variable ne peut stocker qu'un seul caractère à la fois, symbolisé par l'écriture 'a' , 'b' , 'c'… Attention, key est sensible à la casse, c'est-à-dire qu'il fait une différence entre les minuscules et majuscules.
Dans l'exemple suivant, nous allons afficher dans la fenêtre de visualisation le caractère correspondant à la touche qui aura été appuyée sur le clavier de votre ordinateur. Pour ce faire, nous employons la méthode text().
void
draw
(
) {
background
(
204
);
fill
(
0
);
textSize
(
70
);
text
(
key,30
,70
);
}
Par ailleurs, nous pouvons capter dans notre programme le moment où l'utilisateur a appuyé ou relâché une touche du clavier, par le biais des méthodes keyPressed() et keyReleased(). Ces deux méthodes vont être automatiquement appelées par Processing au moment où l'état d'une touche change.
Dans l'exemple suivant, keyPressed() et keyReleased() changent la valeur de la variable y, utilisée pour positionner le caractère à l'écran selon que la touche est appuyée ou relâchée.
int
y =
0
;
void
setup
(
) {
size
(
130
,200
);
textSize
(
80
);
stroke
(
0
);
fill
(
0
);
}
void
draw
(
) {
background
(
204
);
line
(
10
,100
,120
,100
);
text
(
key,35
,y);
}
void
keyPressed
(
){
y =
180
;
}
void
keyReleased
(
){
y =
80
;
}
VII-A-2. Touches spéciales▲
Les touches spéciales comme les flèches (UP, DOWN, LEFT, RIGHT) ou ALT, CONTROL, SHIFT sont quant à elles stockées dans la variable keyCode. Le test if (key == CODED) permet de vérifier si la touche appuyée est une touche spéciale ou non. Dans un programme, il faudra distinguer les deux cas en fonction du type de touche appuyée que l'on veut tester.
Dans l'exemple suivant, nous allons créer une machine à écrire un peu particulière, puisqu'il sera possible de déplacer le texte grâce aux flèches.
À chaque fois qu'une touche sera appuyée, elle sera stockée dans une chaîne de caractère, pour pouvoir être affichée dans le draw() à l'aide de la méthode text().
String s =
""
;
int
x =
50
;
int
y =
50
;
void
setup
(
) {
size
(
200
,200
);
}
void
draw
(
) {
background
(
255
);
fill
(
0
);
text
(
s, x, y);
}
void
keyPressed
(
) {
if
(
key ==
CODED){
if
(
keyCode ==
LEFT) x =
x -
1
;
if
(
keyCode ==
RIGHT) x =
x +
1
;
if
(
keyCode ==
UP) y =
y -
1
;
if
(
keyCode ==
DOWN) y =
y +
1
;
}
else
{
s =
s +
key;
}
}
Notons l'omission des accolades après chaque if. Cette syntaxe est possible si le if ne s'applique qu'à une seule instruction. Nous utilisons par ailleurs une variable de type String dénommé dans cet exemple s dont la fonction est de stocker une suite de caractères, comme des phrases, par exemple.
VII-A-3. Vérification spécifique de l'appui d'une touche▲
Processing nous permet également de faire appel à la variable keyPressed et de la définir. Attention ! Bien que la syntaxe soit identique, elle n'est pas à confondre avec la méthode keyPressed() abordée au début de ce chapitre. Cette variable va nous informer en permanence si une touche est appuyée ou non et pourra être utilisée dans la méthode draw() notamment.
Dans l'exemple suivant, nous allons dessiner un cercle si la touche appuyée est 'c', un carré si la touche appuyée est 'r'.
void
draw
(
)
{
rectMode
(
CENTER);
background
(
128
);
if
(
keyPressed ==
true
)
{
if
(
key ==
'c'
) {
ellipse
(
50
,50
,90
,90
);
}
else
if
(
key ==
'r'
) {
rect
(
50
,50
,90
,90
);
}
}
}
VII-B. Les événements souris▲
Dans ce chapitre, nous allons voir comment interagir avec la souris, en récupérant des informations comme sa position ou bien les actions de clics. Pour illustrer ces fonctionnalités, nous allons créer un petit logiciel de dessin, à base de cercles, en introduisant même du mouvement aléatoire !
VII-B-1. Position de la souris▲
Les coordonnées de la souris dans la fenêtre sont accessibles par les deux variables mouseX et mouseY, disponibles par défaut dans Processing. Elles permettent de connaître la position de la souris par rapport à notre fenêtre de dessin, en prenant pour origine le coin gauche supérieur de la fenêtre. Dans l'exemple suivant, nous allons créer un programme qui va dessiner un cercle à partir de la position de la souris.
void
setup
(
) {
size
(
200
,200
);
smooth
(
);
}
void
draw
(
) {
ellipse
(
mouseX,mouseY,15
,15
);
}
À présent, nous allons modifier très légèrement l'exemple précédent en changeant dynamiquement le rayon du cercle. Celui-ci est choisi au hasard grâce à la méthode random(), qui génère un nombre aléatoirement au sein d'une plage de valeur. À chaque fois que Processing va exécuter draw(), la variable r sera remplie avec un nombre au hasard compris entre 3 et 30 : 12, 25, 23, 11, 22, 4, 10, 11, 25 … Le changement de valeur du rayon va provoquer le tremblotement du cercle autour du curseur de la souris.
void
setup
(
) {
size
(
300
,300
);
smooth
(
);
}
void
draw
(
) {
float
r =
random
(
3
,30
);
ellipse
(
mouseX,mouseY,r,r);
}
Dès que la souris sort de la fenêtre, la position de la souris n'est plus relayée à notre programme. Les cercles se dessinent et s'accumulent à la dernière position captée par notre sketch.
VII-B-2. Clics de souris▲
Nous pouvons intercepter les clics de souris grâce aux méthodes mousePressed() et mouseReleased().
Ces deux méthodes permettent de savoir si l'utilisateur a appuyé ou relâché un des boutons de la souris. Reprenons l'exemple précédent en changeant la couleur de remplissage de notre cercle lorsque l'utilisateur appuie sur un des boutons de la souris. Nous allons choisir un niveau de gris pris au hasard au moment du clic que nous appliquerons au cercle à l'aide de la commande fill().
float
ton_de_gris =
255
;
void
setup
(
) {
size
(
300
, 300
);
smooth
(
);
}
void
draw
(
) {
float
r =
random
(
10
, 80
);
fill
(
grey);
ellipse
(
mouseX, mouseY, r, r);
}
void
mousePressed
(
) {
grey =
random
(
255
);
}
Processing nous permet d'identifier quel bouton de la souris a été appuyé. Pour cela, nous pouvons utiliser la variable mouseButton qui va contenir soit LEFT, RIGHT ou CENTER correspondant au bouton droit, gauche et central (si la souris dispose de tous ces boutons). Utilisons cette variable dans un exemple pour dessiner un cercle qui change de couleur en fonction du type de bouton pressé.
void
setup
(
) {
size
(
200
,200
);
smooth
(
);
}
void
draw
(
) {
background
(
255
);
ellipse
(
100
,100
,200
,200
);
}
void
mousePressed
(
) {
if
(
mouseButton ==
LEFT) fill
(
0
);
if
(
mouseButton ==
RIGHT) fill
(
255
);
if
(
mouseButton ==
CENTER) fill
(
128
);
}
VII-B-3. Autres événements▲
Processing permet de capter deux autres événements souris, notamment lorsqu'elle est en mouvement au-dessus de la fenêtre.
mouseMoved() va permettre de détecter le mouvement de la souris lorsque celle-ci se déplace au-dessus de la fenêtre de dessin. Si le pointeur de souris sort de la zone de la fenêtre ou s'il ne bouge plus, alors la méthode n'est plus appelée.
mouseDragged() est appelée lorsque l'utilisateur a cliqué sur un bouton tout en bougeant la souris au-dessus de la fenêtre. Cette instruction permet notamment de gérer le glisser-déposer, par exemple. mouseDragged() continue d'être actif même si la souris sort de la fenêtre.
Dans l'exemple suivant, nous allons utiliser ces méthodes. Lorsque mouseMoved() est appelé suite à l'action de l'utilisateur, on change la couleur de remplissage du cercle et quand c'est mouseDragged() qui l'est, on ajuste sa taille.
int
r =
100
;
int
c =
100
;
void
setup
(
) {
size
(
255
, 255
);
smooth
(
);
}
void
draw
(
) {
background
(
255
);
fill
(
c);
ellipse
(
width/
2
, height/
2
, r, r);
}
void
mouseMoved
(
) {
c =
mouseY;
}
void
mouseDragged
(
) {
r =
mouseX;
}
VII-B-4. Curseur▲
Parfois il est bien pratique de cacher le curseur de la souris, par exemple dans le cas d'une installation artistique où le pointeur pourrait venir malencontreusement parasiter l'affichage de votre animation. Dans Processing, il existe fort heureusement une instruction pour masquer le pointeur :
noCursor
(
);
Il est également possible de modifier la forme du curseur pour signaler certains événements à l'utilisateur, par exemple changer la forme du curseur au survol d'un élément. Il suffit pour cela d'utiliser cursor() avec comme paramètre une des valeurs suivantes : ARROW, CROSS, HAND, MOVE, TEXT, WAIT. L'exemple ci-après affiche une forme différente de curseur (une flèche, une croix, une main, un sablier) selon la zone de dessin survolée par la souris.
void
setup
(
) {
fill
(
255
);
rect
(
10
, 10
, 20
, 80
);
fill
(
175
);
rect
(
40
, 10
, 20
, 80
);
fill
(
100
);
rect
(
70
, 10
, 20
, 80
);
}
void
draw
(
) {
// Nous vérifions si on survole un des trois rectangles
// et modifions le curseur en conséquence
if
(
mouseX >
10
&&
mouseX <
30
&&
mouseY >
10
&&
mouseY <
90
) {
cursor
(
HAND); // Affiche une main
}
else
if
(
mouseX >
40
&&
mouseX <
60
&&
mouseY >
10
&&
mouseY <
90
) {
cursor
(
ARROW); // Affiche une flèche
}
else
if
(
mouseX >
70
&&
mouseX <
90
&&
mouseY >
10
&&
mouseY <
90
) {
cursor
(
WAIT); // Affiche un sablier
}
else
{
cursor
(
CROSS); // Affiche une croix si on ne survole rien
}
}
VII-C. L'entrée Microphone▲
Si votre sketch peut être programmé pour être sensible à l'appui d'une touche du clavier ou au clic de la souris, ce n'est pas la seule forme d'interactivité offerte par Processing. Vous pouvez également donner à votre programme le sens de l'ouïe en vous servant d'un microphone comme oreille et en faisant correspondre des actions du sketch à certains paramètres du son capté.
Nous allons tout d'abord rappeler certaines notions abordées dans le chapitre consacré à la lecture du son pour ensuite nous concentrer plus spécifiquement sur l'ajout d'une source sonore à l'aide d'un microphone.
VII-C-1. Minim▲
Il a été décidé à un moment donné d'intégrer dans toute distribution Processing la bibliothèque audio Minim pour pouvoir jouer au moins des fichiers audio ou capter le son entrant par le microphone. A priori cette bibliothèque (également appelé bibliothèque en jargon informatique) est déjà installée sur votre ordinateur. Pour vérifier sa présence et l'intégrer dans votre programme, il suffit d'aller dans le menu Sketch > Import Library… > Minim audio.
Selon votre plateforme et votre version, à la suite de cette action, Processing ajoutera des lignes de code plus ou moins nombreuses en haut de votre programme. Par défaut, il faut au moins voir affichée l'instruction suivante :
import
ddf.minim.*;
C'est cette instruction (que vous pouvez saisir à la main si vous le voulez) qui importera l'ensemble des fonctionnalités de la bibliothèque Minim pour les rendre accessibles à notre programme. C'est à partir de cette instruction import ddf.minim.*; que notre sketch va pouvoir jouer du son.
VII-C-2. Configurer minim▲
Le lancement de notre sketch à l'aide du bouton Run ne suffit pas pour jouer un son. Au préalable, il faut prévoir d'activer les fonctionnalités de gestion audio de Minim (son moteur audio en jargon informatique). Pour jouer un son, Minim doit en effet auparavant demander un accès à la carte son de votre ordinateur. Ne vous inquiétez pas, Minim s'occupe de toute cette cuisine interne pour vous, il suffit de lui dire de s'activer pour cela. Le fait d'avoir activé Minim suppose de prévoir de le désactiver à la fin de notre programme.
Si vous ne comprenez pas tout ce que nous venons de dire, ce n'est pas trop grave. Sachez simplement qu'il faut saisir le code suivant au début de chaque programme utilisant Minim :
import
ddf.minim.*;
Minim minim;
void
setup
(
) {
minim =
new
Minim
(
this
);
}
Et qu'il faut également écrire la fin du programme les instructions suivantes :
void
stop
(
) {
minim.stop
(
);
super
.stop
(
);
}
VII-C-3. Ajouter une source sonore▲
Pour exploiter l'entrée microphone, nous devons créer un objet source sonore qui permettra d'accéder au son capté par le microphone.
import
ddf.minim.*;
Minim minim;
AudioInput in;
void
setup
(
) {
minim =
new
Minim
(
this
);
in =
minim.getLineIn
(
Minim.STEREO, 512
);
}
Il faut également prévoir d'arrêter la capture du son ainsi que la bibliothèque Minim à la fin de l'exécution du programme. À cet effet, nous employons la méthode stop();
void
stop
(
) {
in.close
(
);
minim.stop
(
);
super
.stop
(
);
}
VII-C-4. Visualiser le niveau sonore▲
L'objectif à présent est de faire varier la couleur du fond de la fenêtre de l'espace de dessin en fonction des sons captés par le micro. Plus le son sera fort, plus le fond sera blanc. Nous allons utiliser l'objet source audio (in). qui comporte trois sous objets : left, right et mix. Dans le cas d'un microphone stéréo, ces trois éléments représentent le canal gauche, droit et la moyenne des deux. À partir de ces canaux, il est possible de connaître le niveau sonore du son capté à l'aide de la méthode level() qui retourne au programme cette information sous la forme d'une valeur chiffrée.
void
draw
(
) {
background
(
in.mix.level
(
)*
2550
);
}
Une capture d'écran de l'espace de dessin avec un niveau sonore moyen :
VII-C-5. Un petit jeu▲
Nous allons à présent créer un jeu très simple qui va exploiter davantage les possibilités d'interaction avec un microphone. Le jeu sera composé d'une balle partant de la gauche de l'écran et qui subit une force d'attraction lente, mais permanente vers cette partie de l'espace de dessin. En faisant du bruit, l'utilisateur va la pousser vers la droite, son but étant de passer la ligne d'arrivée.
Pour commencer, nous allons changer la taille de notre fenêtre de visualisation de l'espace de dessin, activer le lissage du tracé et définir le contour des formes en lui appliquant la couleur blanche. Le code concerné est signalé en gras.
void
setup
(
) {
size
(
600
, 100
);
smooth
(
);
stroke
(
255
);
minim =
new
Minim
(
this
);
in =
minim.getLineIn
(
Minim.STEREO, 512
);
}
Nous allons ensuite créer une variable qui stockera la position de la balle. Elle sera déclarée en en-tête du programme pour être disponible dans l'ensemble du sketch. Nous lui assignons la valeur 0.
float
ballX =
0
;
void
setup
(
) {
...
Dans la méthode draw(), nous allons définir un fond noir et dessiner une balle qui réagit en fonction du niveau du son. À chaque appel de la méthode draw() nous ajoutons le niveau sonore du microphone à la coordonnée x de la balle. Elle va se mettre en mouvement au fur et à mesure que nous faisons du bruit.
void
draw
(
) {
background
(
0
);
ballX =
ballX +
in.mix.level
(
)*
20
;
ellipse
(
25
+
ballX, height -
25
, 50
, 50
);
}
Pour éviter que la balle ne sorte de l'écran, nous allons ajouter deux conditions qui vont corriger sa position si la variable ballX est plus petite que 0 ou plus grande que la largeur du sketch.
void
draw
(
) {
background
(
0
);
ballX =
ballX +
in.mix.level
(
)*
20
;
if
(
ballX <
0
) {
ballX =
0
;
}
if
(
ballX >
width-
25
) {
ballX =
width-
25
;
}
ellipse
(
25
+
ballX, height-
25
, 50
, 50
);
}
Nous allons ajouter une « ligne d'arrivée » ainsi qu'une condition qui spécifie que si la balle l'a franchie, elle change de couleur.
void
draw
(
) {
background
(
0
);
ballX =
ballX +
in.mix.level
(
)*
20
;
if
(
ballX <
0
) {
ballX =
0
;
}
if
(
ballX >
width-
25
) {
ballX =
width-
25
;
}
if
(
ballX >
500
) {
fill
(
255
, 0
, 0
);
}
else
{
fill
(
255
);
}
line
(
500
, 0
, 500
, 100
);
ellipse
(
25
+
ballX, height-
25
, 50
, 50
);
}
Afin de compliquer le jeu, nous allons ajouter un comportement à la balle qui la fait revenir en arrière en permanence. À chaque appel de la fonction draw() nous allons diminuer légèrement sa position sur l'axe x.
void
draw
(
) {
background
(
0
);
ballX =
ballX -
0.5
;
ballX =
ballX +
in.mix.level
(
)*
20
;
if
(
ballX <
0
) {
ballX =
0
;
}
if
(
ballX >
width -
25
) {
ballX =
width -
25
;
}
if
(
ballX >
500
) {
fill
(
255
, 0
, 0
);
}
else
{
fill
(
255
);
}
line
(
500
, 0
, 500
, 100
);
ellipse
(
25
+
ballX, height -
25
, 50
, 50
);
}
À présent, il ne vous reste plus qu'à faire appel à votre imagination pour utiliser du son dans vos projets et en exploiter les nombreuses possibilités : analyse des fréquences d'un son, création d'effets sonores sur mesure à l'aide de fonctionnalités de synthèse audio, etc.
VII-D. L'entrée vidéo▲
Processing permet de capturer les images provenant d'une caméra vidéo connectée à votre ordinateur par un câble ou à distance via le Wifi ou même internet. Ces images peuvent ensuite être affichées dans la fenêtre de visualisation de votre sketch et le cas échéant, modifiées en fonction de vos besoins. Les applications créatives de l'entrée vidéo sont multiples.
VII-D-1. Les types de caméras supportées▲
Les caméras les plus simples à utiliser sont les caméras USB, souvent appelées caméras web. Il est également possible d'employer des caméras que l'on branche sur un port IEEE1394 (Firewire) comme les caméras DC1394 et les caméras DV.
Pour exploiter l'entrée vidéo, vous pouvez utiliser la bibliothèque video de Processing qui fait appel à la bibliothèque QuickTime, de la compagnie Apple Computer. Toutes les caméras supportées par QuickTime devraient donc fonctionner sans aucune difficulté. Apple fournit cette bibliothèque uniquement pour les systèmes d'exploitation Mac OS X et Microsoft Windows. Sur GNU/Linux, il est préférable d'utiliser la bibliothèque GSVideo. Celle-ci fait usage de l'excellente GStreamer.
VII-D-2. Capturer des images avec la bibliothèque vidéo▲
Sous réserve que vous soyez sur Mac OS X ou Windows, voyons à présent comment utiliser la bibliothèque video. Tout d'abord il faut importer la bibliothèque vidéo en cliquant dans le menu : Sketch > Import Library >… > Video. Une ligne de code s'affichera au début de votre programme :
import
processing.video.*;
Ensuite nous allons déclarer une variable qui stockera notre objet Camera. Dans la méthode setup(), il faut préciser laquelle des caméras présentes sera utilisée. Notez que vous devez déjà avoir connecté et installer cette caméra vidéo sur votre ordinateur pour faire fonctionner cet extrait de code. Si le code génère une erreur au moment de créer la caméra, essayez d'autres numéros de devices[].
Capture camera;
void
setup
(
) {
size
(
640
, 480
);
background
(
0
);
String[] devices =
Capture.list
(
);
println
(
devices);
camera =
new
Capture
(
this
, 320
, 240
, devices[0
]);
}
L'étape suivante consiste à préciser, dans la méthode draw(), que nous affichons la dernière image obtenue par la caméra en vérifiant qu'une nouvelle est bien disponible. En effet, les images sont reçues de la caméra vidéo à une certaine fréquence, par exemple 30 fois par seconde, ce qui ne correspond pas forcément à celle de notre sketch. Dans l'exemple ci-après, nous avons demandé à notre programme d'appliquer sur l'image captée un filtre de solarisation (inversion des valeurs d'ombre et de lumière de l'image).
void
draw
(
) {
if
(
camera.available
(
)) {
camera.read
(
);
camera.filter
(
INVERT);
image
(
camera, 160
, 100
);
}
}
La variable de type Capture peut être utilisée de la même manière qu'une variable de type PImage : il est possible de lire les pixels de l'image, la transformer, l'afficher plusieurs fois à l'écran, et bien d'autres choses encore.
Le programme complet de notre exemple :
import
processing.video.*;
Capture camera;
void
setup
(
) {
size
(
640
, 480
);
background
(
0
);
String[] devices =
Capture.list
(
);
println
(
devices);
camera =
new
Capture
(
this
, 320
, 240
, devices[0
]);
}
void
draw
(
) {
if
(
camera.available
(
)) {
camera.read
(
);
camera.filter
(
INVERT);
image
(
camera, 160
, 100
);
}
}