Bibliothéque interne et le son

tutorial de programmation
en processing

 

 

Bibliothèque OscP5

dessein
  OSC, Ulf Langheinrich

 

L'origine de l'OSC

OSC a été inventé en 1997 au UC Berkeley Center for New Music and Audio Technologies (CNMAT; opensoundcontrol.org). Il prend le relais du protocole Midi. OSC est plus rapide, plus précis et peut contrairement au Midi envoyer plusieurs données et type de données aux mêmes temps. Donc l'OSC a été écrit en particulier pour contrôler du son dans des dispositifs en réseaux mais aussi plus généralement pour les appareils multimedias. Normalement, l'OSC est envoyé via des câbles Ethernet ou bien par Internet. Il est également possible d'envoyer des données d'un logiciel à un autre qui sont tout les deux sur le même ordinateur.

Pour aller plus loin: site OSC
  Intro en englais
 

 

OSC et le réseau

D'abord quelques termes concernant le réseau en général et le OSC en particulier. Dans un réseau on appelle le client celui qui envoie des messages et l'autre qui le reçoit est le server ou host. Le métaphore du restaurant aide de se représenter ces terme: Le client va dans un restaurant et commande quelque chose que le restaurant produit (le plat) après.

Le client doit connaître le numéro IP du host. Il y a plusieurs manière de la connaître

windows 2000, XP et Vista: Par la ligne de commande dans 'executer' (démarrer/executer)
cmd /k ipconfig /all ou simplement ipconfig

Mac: la ligne de commande dans Terminal (Application/Utilities)
ifconfig

Linux: dans une console en tant que root -- en tant qu'utilisateur
ifconfig -- /sbin/ifconfig

Un autre possibilité qui permet aussi de configurer le numéro IP est
PC: ouvrir "connexion réseau"; clic droit sur le réseau local; double clic sur le "Protocole TCP/IP".
Mac: Préférence Système/ Réseau/ Avancé/ TCP/IP

Ici vous pouvez configurer vous même le numéro IP qui est normalement un numéro entre 192.168.0.1 et 192.168.254) Bien sure dans ce cas, chaque machine dans le réseau doit avoir son propre numéro IP. Certains réseaux sont configurer d'une manière que les numéro IP change des temps en temps. Dans ce cas, il est préférable de configurer une l'IP fixe.

Si par exemple pour tester un sketch, on veut envoyer les données à soit même on utilise l'IP 127.0.0.1. Cela est un numéro IP réservé qu'on appelle aussi "localhost".

Le message est envoyé ensuite (si on précis pas d'autres chose) par le User Datagram Protocol (UDP). l'UDP exige encore une port réseau qui est un numéro entre 0 bis 65535.

 

L'OSC dans processing

L'OSC est une bibliothèque externe de Processing écrit par Andreas Schlegel. (plus de détails sur les bibliothèques en générale se trouvent sur la page Sound ). Pour pouvoir l'utiliser il faut d'abord la télécharger ( ici ) est l'installer dans le dossier "Libraries" qui se trouve dans le dossier où sont stocké les sketchs (pour le trouver regarde dans File/Preferences/sketchbook location ) Il est préférable d'installer les exemples dans le dossier ou se trouve l'application Processing. Dans ce dossier se trouve un dossier "exemples/Libraries" c'est ici.

Ensuite il faut intégrer les classes dans le sketch par ce deux lignes, soit manuellement ou par le menu Sketch/ Import Library /OscP5 :

import oscP5.*;
import netP5.*;

Ensuite on peut envoyer et recevoir des données.

Pour aller plus loin: Référence de la library OSC
 
 

 

Envoyer les données (client)

Pour pouvoir envoyer des données, il faut définir un adresse auquel on envoie les envoie et créer et instancier un objet OSC. Pour l'instant on envoie les données à nous même.

OscP5 osc;
NetAddress adresseDistinataire;
void setup() {
osc = new OscP5(this, 10000);
adresseDistinataire = new NetAddress("127.0.0.1",15000);
}

Ensuite on crée un message vide (1). Cette message sera envoyé à un "address pattern" un sort de tiroir précis auquel on adresse le message. Cet adresse est signaler par le trait diagonale /.On rajoute ensuite a cette message la variable de la donnée à envoyer (2). On peut aussi rajouter plusieurs données et même des types différents.(3) On peut envoyer entre autres des boolean, integer, float, char, string... ( liste complet ). A la fin il ne reste que de la poster avec la méthode "send() avec comme argument le nom du message et l'adresse du destinataire.(4).

void mouseDragged() {
OscMessage messageSortant = new OscMessage("/MousePos");
//(1)
// ajouter la position X de la souris
messageSortant.add(mouseX);
//(2)
messageSortant.add(mouseY);
//(3)
// envoyer le message
osc.send(messageSortant, adresseDistinataire);
//(4)
}

Au lieu d'utiliser la méthode send() on peut envoyer le message avec la methode flush(). Dans ce cas, ce n'est pas nécessaire d'instancier l'objet OscP5 et le sketch devient plus léger. Par contre, dans ce cas, le Client ne peut pas recevoir des messages.

Recevoir des données (Server)

Pour recevoir des données, on commence +/- avec les mêmes commandes que pour le Client:

import oscP5.*;
import netP5.*;

OscP5 osc;
NetAddress adresseDistinataire;
void setup() {
osc = new OscP5(this, 15000);

Il faut évidemment instancier l'objet OscP5 avec la porte qui correspond à la porte à qui le client envoie les données.

La méthode qui 'écoute' les messages entrant s'appelle oscEvent et prend comme argument un objet OscMessage. Cet objet a plusieurs fonctions, entre autre la méthode get() avec comme argument l'emplacement du donnée dans la liste d'envoie. Dans notre cas, nous avons envoyé un int et un string, donc deux choses. L'un on utilisera pour bouger une ligne, l'autre on imprimera dans la fenêtre de contrôle.

void oscEvent(OscMessage messageEntrant) {
int posX = messageEntrant.get(0).intValue();
int posX= messageEntrant.get(1).intValue();
line(posX, 0, posX, height);
line(0,posY, 0,posY, width);
}

Trier avec AddrPattern et Typetag

Quand un message est envoyé par OSC, on envoie avec les données aussi des métadonnées suivants: l'address pattern et le typetag. L'address patern a été nommé dans le paragraphe 'client' le typetag est un ou plusieurs lettres qui indique le type de variable du message

OSC Type Tag Type d' argument correspondant
i int et byte
f float
d double
s string
c char
b blob
T ou F boolean

fiss par exemple signifie un message qui contient dans l'ordre un float, un integer et deux string. Ces métadonnées nous permet par exemple de filtrer les messages.


void oscEvent(OscMessage messageEntrant) {
if(messageEntrant.checkAddrPattern("/MousePos")==true) {
if(messageEntrant.checkTypetag("is")) {// un integer et un string

println("reçu un message osc avec l'address pattern "+
messageEntrant.addrPattern()+" typetag "+ messageEntrant.typetag());
}}}

Plug it in!

Il y a un methode plus élégant de trier des messages qui s'appelle plug (brancher). Avec cet méthode on peut envoyer des données directement à une méthode spécifique. Pour cela, nous appelons la methode plug et avec comme argument (l'objet (this), le nom de la méthode, l' address Pattern, et si on veut le typetag. Par la suite on recevoit tous les données qui correspond à cet adressPattern et evt. typetag dans une méthode de ce nom. La méthode doit avoir comme argument évidement les type de donnée correspondant au typetag. Tous les autres données qui ne correspond pas au méthode plug arriveront dans la méthode OscEvent()

import oscP5.*;
import netP5.*;

OscP5 osc;
NetAddress adresse;

void setup(){
osc=new OscP5(this, 1200);
adresse=new NetAddress("127.0.0.1",1200);
osc.plug(this, "plugMethode","/MousePos","is");
// la specification du typetag est optionnel
}

void draw(){
background(255);
}

void plugMethode(int mPos, String s){
line(mPos,0, mPos, height);
println(s+"="+mPos);

}

void mouseDragged() {
OscMessage messageSortant = new OscMessage("/MousePos");
messageSortant.add(mouseX);
messageSortant.add("mouseXposition");
osc.send(messageSortant, adresse);
OscMessage messageSortant2 = new OscMessage("/MousePos");
messageSortant2.add("reste");
osc.send(messageSortant2, adresse);
}

void oscEvent(OscMessage messageEntrant) {
if(messageEntrant.isPlugged()==false) {
String s= messageEntrant.get(0).stringValue();
println("ici ce qui "+s);
}
}
Étant donnée que le message "reste" ne correspond ni au typetag dans la méthode plug, ni aux arguments de la méthode qui reçoit les messages du plug, "reste" est 'forwardé' au méthode oscEvent()


Envoyer par bundle

Un autre façon d'envoyer les messages est par "bundle" (lot, paquet). Au lieu d'envoyer message par message par la méthode OscMessage, il est possible de créer plusieurs message et ensuite de l'envoyer avec l'objet OscBundle. Il faut s'imaginer un petit carton avec les messages qu'on vide d'abord dans un carton plus grande et ensuite en l'envoie tous ensemble. Dans l'absolu, on peut rajouter plusieurs messages/ données dans un envoie traditionnel. L'avantage d'envoie en bundle est qu'on peut envoyer le message avec différents adresse patterns.

void mousePressed() {

OscBundle lotDeMessages = new OscBundle("/mousePos");
OscMessage messageSortant = new OscMessage("/mouse_x");
messageSortant.add(mouseX);
lotDeMessages.add(messageSortant);

// vide le message
messageSortant.clear();

messageSortant.setAddrPattern("/mouse_y");
messageSortant.add(mouseX);
lotDeMessages.add(messageSortant);
oscP5.send(myBundle, adresse);
}

Un autre avantage est qu'on peut rajouter un 'timetag' au message. Le timetag est un metadonnée. La documentation précise:
Time tags are represented by a 64 bit fixed point number. The first 32 bits specify the number of seconds since midnight on January 1, 1900, and the last 32 bits specify fractional parts of a second to a precision of about 200 picoseconds. This is the representation used by Internet NTP timestamps.The time tag value consisting of 63 zero bits followed by a one in the least signifigant bit is a special case meaning "immediately." opensoundcontrol.org

Pour rajouter un timetag au bundle, on utilise la méthode setTimetag() dans laquelle on peut préciser l'heure actuel avec now().

lotDeMessages.setTimetag(lotDeMessages.now() );

sur le serveur on récupère le timetag par la méthode du même nom

messageEntrant.timetag();

 

Broadcasting

Il est possible d'envoyer plusieurs messages au même temps mais aussi de l'envoyer à plusieurs ordinateurs. On s'imagine un journaliste qui envoie son reportage à une station de radio qui le diffuse (broadcasting) après à tous ceux qui allume le radio. Dans un premier temps, on simplifie la tâche en envoyant un message à la "station de radio" (broadcaster). Dans ce message est inclus l'adresse du reporteur. La station rajoute le journaliste à son liste de diffusion en rajoutant quelques informations. Enfin, la station diffuse la le reportage à son liste de diffusion. On avoue que le métaphore du journaliste qui est le seul auditeur de son reportage est un peu triste - c'est bien un métaphore... En plus dans notre exemple, le journaliste envoie comme reportage la position Y de sa souris ;-)

Le code coté journaliste n'a rien de nouveau:

//client - journaliste / broadcastClient
import oscP5.*;
import netP5.*;

OscP5 osc;

NetAddress addressDiffuseur;

int x, y;
void setup() {
size(400,400);
osc = new OscP5(this,12000);// j'ecoute au porte 12000
addressDiffuseur = new NetAddress("192.168.0.4",007);
//IP du radio, j'envoie à James
}

void draw(){
background(0);
ellipse(x, y, 40,40);
}

void mouseMoved() {
OscMessage messageSortant = new OscMessage("/reportage");
messageSortant.add(mouseY);
osc.send(messageSortant, addressDiffuseur);
}

void oscEvent (OscMessage messageEntrant){
if (messageEntrant.checkTypetag("ii")){
x = messageEntrant.get(1).intValue();
y = messageEntrant.get(0).intValue();
}
}

Coté radio, il y a plusieurs nouveauté. Pour enregistrer plusieurs adresse, nous utilisons l'objet NetAddressList qui peuvent stocké donc un liste des adresse IP avec la porte associée.

Pour rajouter l'adresse du journaliste, nous utilisons la méthode add() de NetAddressListe. Ses arguments sont évidement le n° IP du journaliste et la porte par laquelle je veux l'envoyer ensuite. L'IP est inclus dans le message qu'on va récupérer avec la méthode messageEntrant.netAddress().address(). L'objet NetAddress et NetAddressList ne sont malheureusement pas documenté.

Mais maintenant, nous rajoutons à chaque message un numéro IP dans la liste de diffusion et ensuite la radio va diffuser des milliers des fois le même message à la même unique personne. Pour l'eviter, on rajoute un filtre. Pour verifier si l'adresse est déjà dans la liste, on utilise la methode contains, avec les mêmes arguments que dans add()

//Broadcaster - Diffuseur
import oscP5.*;
import netP5.*;

OscP5 osc;
NetAddressList addressListServer = new NetAddressList();

int PorteEcoute = 007;
int PorteEnvoie = 12000;

int y;
void setup() {
  size(400, 400);
  osc = new OscP5(this, PorteEcoute);
}

void draw() {
  background(0);
  ellipse(mouseX, y, 40, 40);
}

void oscEvent(OscMessage messageEntrant) {
if(!addressListServer.contains(messageEntrant.netAddress().address(),PorteEnvoie))
addressListServer.add(new NetAddress(messageEntrant.netAddress().address(), PorteEnvoie));
println(messageEntrant.address());
}
y = messageEntrant.get(0).intValue();
messageEntrant.add(mouseX);
osc.send(messageEntrant, addressListServer);  
}

Il est biensure possible qu'il y a plusieurs journalistes qui envoient des messages-reportages et devient ainsi des auditeurs.

Sur le site de l'auteur de la bibliothèque se trouve un exemple un peu plus sophistiqué, afin de dissocier l'envoie des données du fait de s'abonner à la liste de diffusion. Regardez l'exemple oscP5broadcastClient et oscP5broadcaster.

 

Multicast

En informatique, le terme multicast (multidiffusion) est utilisé pour désigner une méthode de diffusion de l'information d'un émetteur (source unique) vers un groupe (plusieurs supports/médias). On dit aussi diffusion multipoint ou diffusion de groupe.

Les récepteurs intéressés par les messages adressés à ce groupe doivent s'abonner au préalable à ce groupe. Au final, le résultat de ces abonnements est de permettre aux commutateurs et routeurs intermédiaires d'établir une route depuis le ou les émetteurs de ce groupe vers le ou les récepteurs de ce groupe.

On entend par multicast le fait de communiquer simultanément avec un groupe d'ordinateurs identifiés par une adresse spécifique (adresse de groupe). En terme technique, le protocole IGMP (Internet Group Management Protocol) permet à un PC de s'enregistrer au groupe tandis que le protocole IP/Multicast permet d'envoyer un même paquet à plusieurs utilisateurs.

Un groupe multicast se compose d'un ensemble de machines. Il est entièrement dynamique (une station peut rejoindre ou quitter le groupe à tout moment), et ouvert (une station peut émettre un paquet dans un groupe sans en faire partie). Un groupe multicast est désigné par une adresse IP (de 224.0.0.1 à 239.255.255.255). Lorsqu'un poste veut envoyer un paquet à un groupe multicast, il envoie ce paquet à l'adresse IP identifiant ce groupe (par exemple : 224.1.2.3). La réception est réalisée par un routeur abonné au groupe et le paquet est alors dupliqué et renvoyé grâce à une trame de niveau 2 Multicast.

Wikipedia

Il y a rien de plus simple d'envoyer et recevoir les messages à travers un multicast socket. La seul chose qui diffère c'est le n° IP. Dans le cas d'un réseau local, il s'agit d'un n° entre 239.0.0.0 - 239.255.255.255

osc = new OscP5(this,"239.0.0.1",7777);

le reste, comme d'habitude

Pour aller plus loin: Multicast
 
 

 

OSC et PD

Un autre intéret d'OSC est de communiquer entre plusieurs soft. Voir exemple entre Pure data et processing

download patch et sketch

 

Exercice