ヒューマンシーケンサーを作ってみた
キネクトのソフトの書き方、ちょっとだけ分かってきたのでまた一個ソフト作ってみました。
前から一度作ってみたかった身体を使った楽器です。YAMAHAさんのTENORI-ONという楽器を身体全体でやってしまおうというマジキチな楽器です。
システムは下図のような感じです。Kinectで認識した人のシルエットをPC(Processing)で処理してMIDIデータに変換、MIDIデバイスに送信して音を出力しています。MIDIデバイスは何でもよいのですが、ここでは手持ちのmicroKorg XLを使っています。MIDI通信もMacにMIDIポートなんてないので、実際は物理層はUSBで接続されています(通信のプロトコルがMIDIなだけ)。
動画。結構疲れる楽器ですが、それなりのものができました。数人で遊ぶと楽しいんじゃないかなと思います。参加者募集中。
スケールはCマイナーのペンタトニックです。ちょっと弄ればいろんなスケールで遊べると思います。
参考リンク
『Processingの学習ノート』
ドット化するのにコード参考にしました。本当は前自分が作ったやつベースにするつもりだったのですが、こっちの方がエレガントだったのでドット化の部分はどちらかというとこちらがベースです。
『サウンド&モーション&映像をリアルタイムに同期! 2台のKinectを使ったプロジェクション作品「The V Motion Project」 』
Kinect2台使ったエア楽器。今回の楽器はこいつにインスパイアされています。もう1台Kinect買ったらこれやってみたいな。
ソースコードと使い方
ソースコードは長いので、見たい人だけ続きを読んで下さい。たいしたもんじゃないです。
今回は珍しくClassとか使ってます。改心しました。
使うときはKinectの環境準備の他に、PromidiとOpenCVのライブラリが必要です。
使い方は起動するだけ。スペースキー押せば動画が生成される親切設計。スピードを変えたいときは、count_waitの数字を変えて下さい。
import promidi.*; import SimpleOpenNI.*; import hypermedia.video.*; import processing.video.*; MidiIO midiIO; MidiOut midiOut; SimpleOpenNI context; OpenCV opencv; MovieMaker mm; int fps = 30; int d_circle = 15; int cont = 0; int dot_line = 0; int count_wait = 4; int h_max; int w_max; DotSound[] DotSounds; class DotSound{ int x, y, d; int note; color c; DotSound(int x, int y, int note, color c, int d){ this.x = x; this.y = y; this.note = note; this.c = c; this.d = d; } int getX(){ return this.x; } int getY(){ return this.y; } void soundOut(int x){ if(this.x == x){ if (red(c) == green(c) & green(c) == blue(c)){ }else{ midiOut.sendNote(new Note(note, 10, 3)); } } } void setColor(int c){ this.c = c; } void drawCircle(int x){ if(this.x == x){ // invert color if (red(c) == green(c) & green(c) == blue(c)){ fill(255, 255, 255); }else{ fill(255-red(c), 255-green(c), 255-blue(c)); } }else{ fill(c); } ellipse(this.x*d + d/2, this.y*d + d/2, d, d); } void SetPentaScale(){ int tmp = 0 ; // C if(y%5 == 0){ tmp = y/5*12; } // D# if(y%5 == 1){ tmp = y/5*12 + 3; } // F if(y%5 == 2){ tmp = y/5*12 + 5; } // G if(y%5 == 3){ tmp = y/5*12 + 7; } // A# if(y%5 == 4){ tmp = y/5*12 + 10; } tmp += 60; if(tmp > 127){ tmp -= 128; } this.note = tmp; } } void setup(){ InitKinect(); InitMIDI(); size(context.depthWidth() , context.depthHeight()); InitDotSound(); mm = new MovieMaker(this, context.depthWidth(), context.depthHeight(), "DotMusic.mov", fps, MovieMaker.VIDEO, MovieMaker.LOSSLESS); frameRate(fps); } void InitKinect(){ context = new SimpleOpenNI(this); if(context.enableDepth() == false) { println("Can't open the depthMap, maybe the camera is not connected!"); exit(); return; } if(context.enableRGB() == false) { println("Can't open the rgbMap, maybe the camera is not connected or there is no rgbSensor!"); exit(); return; } context.enableScene(); // matching depth map with rgb map context.alternativeViewPointDepthToImage(); } void InitMIDI(){ midiIO = MidiIO.getInstance(); midiIO.printDevices(); midiOut = midiIO.getMidiOut(0,1); } void InitDotSound(){ h_max = height / d_circle; w_max = width / d_circle; DotSounds = new DotSound[w_max * h_max]; for(int y = 0; y < h_max; y += 1) { for(int x = 0; x < w_max; x += 1) { DotSounds[x + y*w_max] = new DotSound(x, y, 0, 0, d_circle); DotSounds[x + y*w_max].SetPentaScale(); } } } void draw(){ context.update(); // PImage TargetImg = context.rgbImage(); PImage TargetImg = context.sceneImage(); TargetImg.loadPixels(); for(int y = 0; y < h_max; y += 1) { for(int x = 0; x < w_max; x += 1) { DotSounds[x+y*w_max].setColor(TargetImg.pixels[y*width*d_circle + d_circle/2 + x*d_circle+d_circle/2]); DotSounds[x+y*w_max].drawCircle(dot_line); if(cont == 0){ DotSounds[x+y*w_max].soundOut(dot_line); } } } if(cont < count_wait){ cont++; }else{ dot_line++; if(dot_line > w_max){ dot_line = 0; } cont = 0; } mm.addFrame(); } void keyPressed() { if (key == ' ') { mm.finish(); println("save movie."); exit(); } }