数年前に秋葉原で安くポケット・ミク (http://otonanokagaku.net/nsx39/) を入手したもののまともに使ったことがなかったのですが、ようやくそれらしい使い方をしてみました。詳しいドキュメントとしては「ポケット・ミク カスタマイズガイド」 (http://otonanokagaku.net/nsx39/data/nsx39midiguide.pdf) (以降、「カスタマイズガイド」と略) があるのですが、簡単なソースコードもあれば良いなと思ったので記事にしてみました。
なお、MIDI 操作のライブラリとしては RtMidi Version 4.0.0 (https://www.music.mcgill.ca/~gary/rtmidi/) を使いました。
RtMidi のコンパイル
RtMidi をダウンロードして解凍してコンパイルしておきます。
$ wget http://www.music.mcgill.ca/~gary/rtmidi/release/rtmidi-4.0.0.tar.gz
$ tar zxf rtmidi-4.0.0.tar.gz
$ cd rtmidi-4.0.0
$ ./configure
$ make
以降はこのディレクトリ内で作業をすることにします。 改めてビルドコマンドを見たらこの作業は必要ありませんでした…。
プログラム
それでは、任意の歌詞で歌わせることを目指してプログラムをしていきましょう。これが出来れば使うコマンドを変えることで任意の操作を行うことが出来るはずです。
歌詞データを送信する
カスタマイズガイド 8 ページ目にある「2‐2システムエクスクルーシブによる歌詞入力」を行うコードを以下に示します。書かれているバイト列を配列に詰めて送信しています。
歌詞コードはカスタマイズガイドの 10 ページ目の「表2 文字テーブルリスト」にあります。なぜか「こいんにちわ…」になっています。
#include <unistd.h>
#include "RtMidi.h"
#define SLEEP( milliseconds ) usleep( (unsigned long) (milliseconds * 1000.0) )
int main(int argc, char** argv)
{
RtMidiOut *midiout = new RtMidiOut();
unsigned char lyric[] = {
0xF0, 0x43, 0x79, 0x09, 0x11, // SysEx 記述開始
0x0A, // 歌詞入力
0x01, // 歌詞スロット番号
0x09, 0x01, 0x7B, 0x40, 0x36, 0x77, 0x00, 0x70, 0x0A, 0x2D, 0x02, // 「こいんにちわありがとう」
0xF7, // SysEx 記述終了
};
// Check available ports.
unsigned int nPorts = midiout->getPortCount();
if ( nPorts == 0 ) {
std::cout << "No ports available!\n";
goto cleanup;
}
// Open first available port.
midiout->openPort( 0 );
midiout->sendMessage( lyric, sizeof( lyric ) );
// Clean up
cleanup:
delete midiout;
return 0;
}
コンパイル、実行する
RtMidi のドキュメントの Compiling(https://www.music.mcgill.ca/~gary/rtmidi/index.html#compiling) の項参照してコンパイルして実行してみましょう。 OS X だと次のようになります。 midiprobe
midiprobe.cpp
は適宜変更してください。
しかし、まだ発音の処理を書いていないので、何も歌いません。
$ g++ -Wall -D__MACOSX_CORE__ -o midiprobe midiprobe.cpp RtMidi.cpp -framework CoreMIDI -framework CoreAudio -framework CoreFoundation
$ midiprobe
入力した歌詞を順番に発音させる
次に実際に発音するようにしてみましょう。歌詞送信に続き、 NOTE_ON
コマンドを送信することで発音が始まります。
さらに MOJI_SET_KASHI
コマンド REVOICE
コマンド KASHI_POS_INC
コマンドを送信し「読み上げる文字を待機」「再発音」「読み上げ開始位置を1文字進める」を 0.3 秒間隔で実行し、最後に NOTE_OFF
コマンドを送信して発音を終了するようにしたコードが以下です。
#include <unistd.h>
#include "RtMidi.h"
#define SLEEP( milliseconds ) usleep( (unsigned long) (milliseconds * 1000.0) )
int main(int argc, char** argv)
{
RtMidiOut *midiout = new RtMidiOut();
unsigned char lyric[] = {
0xF0, 0x43, 0x79, 0x09, 0x11, // SysEx 記述開始
0x0A, // 歌詞入力
0x01, // 歌詞スロット番号
0x09, 0x01, 0x7B, 0x40, 0x36, 0x77, 0x00, 0x70, 0x0A, 0x2D, 0x02, // 「こいんにちわありがとう」
0xF7, // SysEx 記述終了
};
unsigned char note_on[] = {
0xF0, 0x43, 0x79, 0x09, 0x11, // SysEx 記述開始
0x0D, // コマンド直接入力
0x08, 0x09, 0x00, 0x00, // NOTE_ON コマンド
0xF7, // SysEx 記述終了
};
unsigned char note_off[] = {
0xF0, 0x43, 0x79, 0x09, 0x11, // SysEx 記述開始
0x0D, // コマンド直接入力
0x08, 0x08, 0x00, 0x00, // NOTE_OFF コマンド
0xF7, // SysEx 記述終了
};
unsigned char kashi_revoice_inc[] = {
0xF0, 0x43, 0x79, 0x09, 0x11, // SysEx 記述開始
0x0D, // コマンド直接入力
0x09, 0x05, 0x00, 0x00, // MOJI_SET_KASHI コマンド
0x08, 0x01, 0x00, 0x00, // REVOICE コマンド
0x09, 0x01, 0x00, 0x00, // KASHI_POS_INC コマンド
0xF7, // SysEx 記述終了
};
// Check available ports.
unsigned int nPorts = midiout->getPortCount();
if ( nPorts == 0 ) {
std::cout << "No ports available!\n";
goto cleanup;
}
// Open first available port.
midiout->openPort( 0 );
midiout->sendMessage( lyric, sizeof( lyric ) );
midiout->sendMessage( note_on, sizeof( note_on ) );
for (int i = 0; i < 12; i++) {
SLEEP (300);
midiout->sendMessage( kashi_revoice_inc, sizeof( kashi_revoice_inc ) );
}
SLEEP (300);
midiout->sendMessage( note_off, sizeof( note_off ) );
// Clean up
cleanup:
delete midiout;
return 0;
}
「きよしこの夜」を歌わせる
音程を変化させながら「きよしこの夜」を歌わせるためにデータ構造などを整理したコードはこちらになります。
さらにビブラートや音の強弱をつければ、さらに表情豊かに歌わせる事ができると思います。
感想
興味はあったものの DTM を含めてボーカロイド自体を使ったのは初めてでした。使ってみて初めて
- 何度も歌ってくれる
- 何度も調整させてくれる
- いつでも歌ってくれる
などの機械ならではの良さを実感することが出来ました。特に楽器使いにとってはいつでもボーカルと合わせて練習する楽しさを味わえるところが嬉しいです。