ポケミクをドラムマシン化

こち亀

 今週のジャンプのこち亀でポケット・ミクさんが取り上げられていましたね。結構ガッツリ取り上げられていて驚きました。このサイトも少しはアクセス数増えるかなと思ったのですが、特にそんなことはありませんでした。もうちょっと盛り上がってもよいのに。

公式ムック本

歌うキーボード ポケット・ミク 公式ブック (Gakken Mook)

歌うキーボード ポケット・ミク 公式ブック (Gakken Mook)

 公式本も出ました。それほど新しい情報無く、本に公式Webサイト参照と書いてあるのにまだサイトが更新されてなかったりとアレっと思う箇所はあるのですが、大物へのインタビュ等あるので、ファンならとりあえず買っておけばよいでしょう。

コマンドスロット

 ポケミクの公式本で一つよかったのは、「コマンドスロット」の使い方が分かったこと。コマンドスロットは、ポケミクの起動時、ボタンを押した時等に実行される一連の動作のことです。まあマクロのようなものと思って下さい。この「コマンドスロット」をポケミクに特定のMIDIデータを送ることでこのマクロを書き換えることができるというものです。
 これを弄ると、起動時にポケミクが喋る声を変えたり、あるボタンを押したら特定の音を出す等、ポケミクを自由にカスタマイズできるようになります。

コマンドスロットの具体的な使い方

 コマンドは以下のようなフォーマットになります。

0xF0, 0x43, 0x79, 0x09, 0x11, 0x0C, 0x(Command Slot Number), 0x(Data), 0xF7

Command Slot Numberはコマンドスロットのナンバーで0〜127まであります。例えば0(0x00)だと起動時のコマンドスロット、17(0x11)だとSHIFTボタンを押した時のコマンドスロットです。

 コマンドスロットの対応表は公式サイトでは実はまだ公開されていないのですが、偉大な先駆者が解析済ですので、偉大な先駆者に最大の敬意を払いつつ参考にさせていただきましょう。

ポケミク部屋

 その後のDataにはコマンドスロットの中身の一連のコマンドを記載します。ここには8bitx3のMIDIコマンドを7bitx4の形式に変換しないという鉄の掟があります。まあこの世界ではよくあることです。変換考えるの面倒くさいとか、変換の仕方がわからない人もいるかもしれませんが、心配することはありません。偉大な先駆者が変換のスクリプトを作ってくれています。

ポケミク部屋

 例えば、トライアングルをの音を鳴らすMIDIコマンド ノートオン10ch/ ノートno.81/ベロシティ101((0x99, 0x51, 0x65)を入力してcommand->data変換ボタンを押すと一瞬で (0x04, 0x65, 0x22, 0x65)に変換してくれます。この人ほんとうに凄い…感謝です。

ポケミクドラム作成

 ここまでの知識を応用して、ポケミクをドラムマシンにしてみようと思います。ボタンを押すとドラムの音を出すだけの簡単なものです。

動画


ポケットミクでドラムマシン #大人の科学ポケミク

 簡単にできました。ただし、複数のボタンを同時に押しても同時発音できませんでした、グヌヌ…考えてみればそうだけど、つくるまで気づきませんでした。

関連記事

ポケット・ミク関連情報まとめ - karaage. [からあげ]

 みんな大好き、ソースコード見たい人は続きをクリックして下さい。

ソースコード

 Processingで作ったソースコード。ただコマンドを流し込んでいるだけです。同じコマンドをMIDIシーケンサーとかで送っても同じことができるはずです。

import themidibus.*;
import de.humatic.mmj.*; // OSX specific
 
//// Please refer http://www.humatic.de/htools/mmj/doc/
//CoreMidiDevice[] cmd;    // OSX specific
MidiOutput mo_vocaloid;  // OSX specific
MidiSystem ms;           // OSX specific
 
MidiBus vocaloid;
//int channel = 0; //0:eVocaloid
//int pitch = 0;
//int velocity = 0;

// VIBRATO 
byte[] data_1 = {(byte)0xF0, (byte)0x43, (byte)0x79, (byte)0x09, (byte)0x11, (byte)0x0C,  // header(SysEx)
                 (byte)0x10, (byte)0x04, (byte)0x64, (byte)0x48, (byte)0x65, (byte)0xF7  // Bass Drum 1
                };

// SHIFT
byte[] data_2 = {(byte)0xF0, (byte)0x43, (byte)0x79, (byte)0x09, (byte)0x11, (byte)0x0C,  // header(SysEx)
                 (byte)0x11, (byte)0x04, (byte)0x64, (byte)0x54, (byte)0x65, (byte)0xF7  // Closed Hi-Hat
                };

// A
byte[] data_3 = {(byte)0xF0, (byte)0x43, (byte)0x79, (byte)0x09, (byte)0x11, (byte)0x0C,  // header(SysEx)
                 (byte)0x0B, (byte)0x04, (byte)0x64, (byte)0x5C, (byte)0x65, (byte)0xF7 // Open Hi-Hat
                };

// I
byte[] data_4 = {(byte)0xF0, (byte)0x43, (byte)0x79, (byte)0x09, (byte)0x11, (byte)0x0C,  // header(SysEx)
                 (byte)0x0C, (byte)0x04, (byte)0x64, (byte)0x66, (byte)0x65, (byte)0xF7 // Ride Cymbal
                };

// U
byte[] data_5 = {(byte)0xF0, (byte)0x43, (byte)0x79, (byte)0x09, (byte)0x11, (byte)0x0C,  // header(SysEx)
                 (byte)0x0D, (byte)0x04, (byte)0x64, (byte)0x52, (byte)0x65, (byte)0xF7 // Low Floor Tom
                };


// E
byte[] data_6 = {(byte)0xF0, (byte)0x43, (byte)0x79, (byte)0x09, (byte)0x11, (byte)0x0C,  // header(SysEx)
                 (byte)0x0E, (byte)0x04, (byte)0x64, (byte)0x5E, (byte)0x65, (byte)0xF7 // Low-Mid Tom
                };

// O
byte[] data_7 = {(byte)0xF0, (byte)0x43, (byte)0x79, (byte)0x09, (byte)0x11, (byte)0x0C,  // header(SysEx)
                 (byte)0x0F, (byte)0x04, (byte)0x64, (byte)0x64, (byte)0x65, (byte)0xF7 // High Tom
                };

void setup() {
  size(400, 200);
  background(255);
  MidiBus.list();
  //  vocaloid = new MidiBus(this, "NSX-39 " , "NSX-39 ");

  detectMidiDevices();
}

void detectMidiDevices(){
  String midiDevices[] = MidiSystem.getOutputs();
  String pokemikuName = "NSX-39  - NSX-39 ";
  int devNumb = 0;
  int detect = 0;
  for(int i = 0; i < MidiSystem.getNumberOfOutputs(); i++){
      println(i);
      println(midiDevices[i]);
      println("--------------------");
    
    if(midiDevices[i].equals(pokemikuName)){
      println("NSX-39 OK");
      devNumb = i;
      detect = 1;
    }else{
      println("NSX-39 NG");
    }
  }

  mo_vocaloid = ms.openMidiOutput(devNumb);     // OSX specific
}

 
void draw() {  
  // draw info
  fill(0,0,0);
  text("Press d -> dram setting", 15, 50);
}

void keyPressed(){
  if(key == 'd'){
    mo_vocaloid.sendMidi(data_1);
    mo_vocaloid.sendMidi(data_2);
    mo_vocaloid.sendMidi(data_3);
    mo_vocaloid.sendMidi(data_4);
    mo_vocaloid.sendMidi(data_5);
    mo_vocaloid.sendMidi(data_6);
    mo_vocaloid.sendMidi(data_7);
  }

}

void exit() {
  //  vocaloid.sendNoteOff(channel, pitch, velocity);
  vocaloid.close(); // bye bye!
  super.exit();
}