Pianoptique

De Centre de Ressources Numériques - Labomedia
Aller à : navigation, rechercher

Présentation

Connue sous le nom de "Piano à roulette", "Wheeled piano" ou encore d'"Orgue Barbare", le Pianoptique est un dispositif qui permet de faire de la musique via l'analyse d'un flux vidéo.

Il fonctionne comme un orgue de barbarie où la partition serait lue par une caméra.

Ci-dessous, un rouleau d'orgue de barbarie :

Peforated paper.png

Puis, une interface MIDI qui reprend le même principe :

Piano roll.png

Enfin, l'interface de lecture du Pianoptique :

Screen WP.png

Quelques photos/vidéo ici.

Versions

Il existe trois versions différentes du Pianoptique :

Puredata

C'est la première version qui a été codée et celle qui, à ce jour, est présentée en exposition. La version initiale ne produit pas de son, mais envoie des instructions MIDI qui peuvent être interprétées par un synthé.

Un guide d'installation pour Linux est disponible en anglais sur cette page.

Processing

La version Processing ne produit pas non plus de son mais envoie des messages OSC.

Elle se contente aussi de ne lire que les niveaux de gris.

// Piano à roulette pour Processing
// Copyright 2013 Olivier Baudu 
// publié sous les termes de la license GPL v3.0
// peut donc être utilisé, copié, modifié et diffusé à loisir
 
import oscP5.*;
import netP5.*;
import processing.video.*;
import controlP5.*;
 
Capture cam;
int startMilieu, nbNote, nbPixels, seuil;
boolean [] noteOnOff;
 
OscP5 oscP5;
NetAddress pourPython;
 
ControlP5 cp5;
float contraste = 0.2;
 
void setup() {
 
  //Slider
  cp5 = new ControlP5(this);
  cp5.addSlider("contraste")
    .setPosition(20, 20)
    .setColorBackground(0)
    .setColorActive(color(255, 0, 0))
    .setColorForeground(color(150, 150, 150))
    .setColorCaptionLabel(color(150, 150, 150))
    .showTickMarks(true)
    .setRange(0, 1)
    ;
 
  oscP5 = new OscP5(this, 12000);
  pourPython = new NetAddress("127.0.0.1", 15000);
 
  size(640, 480);
  cam = new Capture(this, 640, 480);
  cam.start();
 
  startMilieu = width * (height/2);
  nbNote = 20;
  nbPixels = width/nbNote;
  seuil = 40;
 
  noteOnOff = new boolean[nbNote];
 
  for (int i=0; i<nbNote; i++) {
    noteOnOff[i] = false;
  }
  noStroke();
}
 
void draw() {
 
  if (cam.available() == true) {
    cam.read();
    cam.filter(THRESHOLD, contraste);
    image(cam, 0, 0);
 
 
    for (int i=0; i<nbNote; i++) {
 
      int somme = 0;
 
      for (int j=0; j<nbPixels; j++) {
        color c = cam.pixels[startMilieu + i*nbPixels + j];
        somme += brightness(c);
      }
 
      int moyenne = somme/nbPixels;
      fill(moyenne);
 
      if (moyenne < seuil) {
 
        boolean newValue = true;
        if (newValue != noteOnOff[i]) {
 
          noteOnOff[i] = newValue;
          fill(255, 0, 0);
 
          OscMessage myMessage = new OscMessage("/playMe");
          myMessage.add(i);  
          oscP5.send(myMessage, pourPython);
        }
      }
      else {
        boolean newValue = false;
        if (newValue != noteOnOff[i]) {
          noteOnOff[i] = false;
        }
      }
 
      rect(i*nbPixels, height/2, nbPixels, (moyenne-255)/1.5);
    }
  }
}

Pianoptique-P5.png

Un exemple de script Python qui écoute l'OSC craché par le sketch ci-dessus (mais qui ne produit pas de son):

#Gestion de l'OSC
from twisted.internet import reactor
from txosc import osc
from txosc import dispatch
from txosc import async
 
def piano_handler(message, address):
 
    print("Je joue la note "+ str(message.getValues()[0]))
 
class UDPReceiverApplication(object):
    """
    Example that receives UDP OSC messages.
    """
    def __init__(self, port):
        self.port = port
        self.receiver = dispatch.Receiver()
        self._server_port = reactor.listenUDP(self.port, async.DatagramServerProtocol(self.receiver))
        print("Listening on osc.udp://localhost:%s" % (self.port))
        self.receiver.addCallback("/playMe", piano_handler)
 
if __name__ == "__main__":
    #initialisation ()
    app = UDPReceiverApplication(15000)
    reactor.run()

Python + OpenCV

Cette version a été écrite pour pouvoir être utilisée sur une Raspberry Pi. En effet, GEM (lib vidéo de Puredata) ne tourne pas avec l'OpenGL ES et les problèmes liés à la vidéo sur Processing + RasPi ne sont pas encore résolus. Nous avons donc porté le scketch Processing en Python en nous appuyant sur OpenCV pour faire l'acquisition vidéo.

Pour installer OpenCV :

sudo apt-get install python-opencv

Vous trouverez des explications ici sur comment nous nous y sommes pris pour exploiter OpenCV pour nos besoins.

import cv2
 
# setup video capture
cap = cv2.VideoCapture(0)
ret,im = cap.read()
 
height,width = im.shape[:2]
milieu = height/2
 
seuil = 40
 
nbNote = 8
nbPixels = width/nbNote
 
noteOnOff = []
for i in range(nbNote):
    noteOnOff.append(False)
 
while True:
    ret,im = cap.read()
    gray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)   
 
    for i in range(nbNote):
 
        somme = 0
 
        # calcul de la moyenne de niveau de gris
        for j in range(nbPixels):
            somme += gray[milieu][i*nbPixels + j]
 
        moyenne = somme/nbPixels
 
        # test du seuil
        if (moyenne < seuil):
            newValue = True
            if (newValue != noteOnOff[i]):
                noteOnOff[i] = newValue
                print ("Je dois jouer la note " + str(i))
 
        else:
            newValue = False
            if (newValue != noteOnOff[i]):
                noteOnOff[i] = newValue  
 
        # dessin des graduations
        for j in range(nbPixels):
            for k in range(4):
                gray[milieu+k][i*nbPixels + j] = moyenne
 
 
    cv2.imshow('video test',gray)
    key = cv2.waitKey(10)


Configuration de la Raspberry Pi

L'idée ici est de faire jouer les notes de musiques par des servomoteurs sur un carillon.

Nous passons pour cela par les cartes PCA9685 - 16-Channel de chez Adafruit. Le protocole utilisé étant l'I2C, il est nécessaire d'installer le paquet python-smbus

sudo apt-get install python-smbus

Sur une Raspian, il est nécessaire d'effectuer ces manips supplémentaire pour pouvoir utiliser l'I2C avec les cartes d'Adafruit:

  • commenter la ligne blacklist i2c-bcm2708 du fichier raspi-blacklist.conf (placer un # devant)
sudo nano /etc/modprobe.d/raspi-blacklist.conf
  • ajouter deux lignes supplémentaires au fichier modules :
sudo nano /etc/modules
i2c-dev
i2c-bcm2708

Relier la Raspberry à la carte Adafruit comme sur la photo ci-dessous. (à noter que l'ordre est inversement-conservé et qu'il est donc possible d'utiliser une nappe standard).

Raspberry-PCA9685.jpg