マリオシーケンサをポケミクに対応した

先日、公開したマリオシーケンサのWebアプリですが当初から本当の目的はポケミクに歌わせることでした。
思えば4月?にポケミクが出ると同時に買って以来、何をしようか考え始め、Web MIDI API周辺を調べ始めたのが発端でした。
ポケミクをもっと簡単に歌わせたい、しかし私には音楽の才能が無い、どうしようかと考えたのです。
マリオシーケンサを思い出すのは時間がかかりませんでした。


マリオシーケンサのMSQファイルの構造は非常に簡単で解析に時間はかかりませんでした。
マリオシーケンサにはFの音しか入っておらず変調して用いるのは直ぐに想像できました。
そこからWeb Audio APIを勉強し始め、HTML5Rocksを読んだり、O'Reillyの本が運良くWebで公開されていたのを全部読んだりなどしてWeb Audio APIと以前に学んだCanvasでマリオシーケンサをWebアプリにすることは可能だと睨んだのが4月終わりから5月くらいだったのでしょうか。そこからは一月あっという間でした。とりあえずWAVのみ版を公開したのが5月26日でした。



で、7月、8月も半分づつ1月この件に打ち込んだ結果がこれです。相変らず前振が長い (^^;
http://minghai.github.io/MikuMikuSequencer/?url=NikoNiko_suite.json&auto=true

ソースコードGitHubのいつもの場所に置いてあります。

https://github.com/minghai/MikuMikuSequencer


名前はMiku Miku Sequencerに変えてあります。
あのMMDにあやかっている様ですが、実際にはポケミクシーケンサと名付けようとしていた所、既にその名前でプログラムを作成している方がいらしたという理由もありました。
本人が恥しくて、あまりこの名前を口にしていません(笑

(注意) 相変わらずChromeでしか動きません。Firefoxでは謎のエラーが出るのでとりあえず放ってあります。ChromeならAndroidでも動いています。


ポケミク対応版を実行するにはポケミクを接続することが必須なのでYoutubeで公開した録画を置いておきます。




使い方

まずは次のチュートリアルビデオを参照して下さい。


はてな日記の組込画面では小さいのでできればYoutubeで別画面として開いて見て下さい。
上の組込ビデオのYoutubeのロゴを押すとYoutubeサイトで開き直すことができます。


ご覧の通り、基本的な使い方は非常に簡単です。

WAVかMIDIの音色で曲を完成し、ミクの音を載せて歌詞をテキストエリアに入力すればそのまま歌います。

ただし、以前のマリオシーケンサに比べ、MIDI音源を利用しているため以下の制約があります。

  • MIDI音源は減衰が非常に小さい、または減衰しない場合があり、音が無限長になります
  • MIDI音源でもキャンセルは効かしてあります。しかし音が連続しない場合にキーオフの指定が必要です
  • ミクは同時に一音しか発声できません。これはポケミクの現状の制約です
  • MIDIのチャンネル1から15がそのまま音色選択にマッピングされています
  • MIDIチャンネルの音色は後述のコマンドを用いて変更が可能です
  • MIDIのチャンネル1はミク専用で変更できません
  • MIDIのチャンネル10はドラムマップ専用で変更できません
  • MIDIのキーは画面上の一番低いド(C4)が60です
  • MIDIのチャンネル10(ドラムマップ)のみ一番低いB3がキー35にマップされます。これはドラムを優先するためです。

上記の制約の一部に対応するために、MMSではキーボードとマウスを用いて個別の音に属性を与える必要があります。

音の属性

音には現在、以下の属性が存在します。

長さ
長さで最も良く使用するのは実際にはキーオフの機能が当てられた長さ0です。デフォルトは無限長でキーオフしなければ音が止まりません。残りはスタッカート用の0.5です。
オクターブ
音の高さは楽譜上の位置で決定しますが、楽譜の領域を越えた高さが必要な場合にはオクターブを相対的に指定します。現在の位置から上下にオクターブを設定可能です。
半音
音には半音の上げ下げが指定できます。ただし、EとFやBとCのように間に半音が存在しない場合には音の位置がずれる場合があります。


音の属性としては後はボリューム(MIDIの属性としてはvelocity)の実装が考えられますが、現在は実装されておりません。

ミクのみには音の属性としてフォネティックシンボル(Phonetic Symbol)が歌詞として別に与えられ、各音に異なった発声が行われます。

キーボード

以下、使用するキーについて説明します。

PgUP, PgDn or [,] or I,O
楽譜を前、または後ろに1ページめくる
<,> or ←,→
楽譜を棒1本左右にずらす
w
(音にカーソルを合わせた状態で)長さ0、またはキーオフの指定
e
(音にカーソルを合わせた状態で)長さ0.5、staccato
q
(音にカーソルを合わせた状態で)長さ指定の取消
+,-
(音にカーソルを合わせた状態で)オクターブを1つ上げる、または下げる
Shift
(楽譜に音を置く時に左クリックと同時に押すことで)音を半音上げる
Ctrl
(楽譜に音を置く時に左クリックと同時に押すことで)音を半音下げる
T
テストプレイ。画面上に表示されている音のみを演奏します
Shift+T
テストプレイ。画面上に表示されている音から継続して終わりまで演奏します。画面上のSTOPボタンを押すことで止められます


音にカーソルを合わせた状態は、マウスで赤い四角形のカーソルを楽譜上の音の四方にきちんとはまるように位置して下さい。
正しく実行できればクリック音が鳴り、音には属性が表示されるようになります。

オクターブの上げ下げは楽譜上で表示できる場合には属性の変化ではなく位置の変化として表われます。

音の半音上げ、下げはEとF、BとCの間では位置の変化として表われる場合があります。

歌詞


歌詞の入力は画面右側のテキストエリアに必ず「ひらがな」で入力します。
歌詞の入力にはミクを歌わせたい意図のとおりに入力しなければなりません。
例えば

  • まばたきしては → まばたきしてわ
  • いきたい → ゆきたい

等が挙げられます。


歌詞の入力にはポケミクで定められた決められた文字しか利用できません。
以下の128文字が入力可能です。


引用元:http://www.din.or.jp/~boya/eVocaloid/pokemiku.html

  0 1 2 3 4 5 6 7 8 9 A B C D E F
0 きゃ
1 きゅ きょ ぎゃ ぎゅ ぎょ すぃ ずぃ しゃ
2 しゅ しぇ しょ じゃ じゅ じぇ じょ てぃ とぅ でぃ
3 どぅ てゅ でゅ ちゃ ちゅ ちぇ ちょ つぁ つぃ つぇ つぉ
4 にゃ にゅ にょ
5 ひゃ ひゅ ひょ びゃ びゅ びょ ぴゃ ぴゅ ぴょ ふぁ
6 ふぃ ふゅ ふぇ ふぉ みゃ みゅ みょ
7 りゃ りゅ りょ うぃ うぇ うぉ ん\ んm んj んn


上と左の数値は内部的にどのような数値に変換されるかを示すものですので無視して大丈夫です。
この表を見るとおわかりになるかと思いますが、普通のひらがなの順と違います。
例えば「し」は「すぃ」を使うことが適切な場合が多くなります。
しかし、多くの場合はどちらを使ってもあまり違いがわからないので最初の内は気にせずとも良いかと思います。

また「を」が無く「うぉ」を使わなければならないのですがプログラム内部で書き換えているため「を」を使用して頂いてかまいません。
同様に「づ」が無く「どぅ」を使わねばなりませんがプログラム内部で書き換えているので「づ」を使用して頂いてかまいません。


ポケミクでは同じ字が続く場合には子音が発音されないという仕様があります。
例えば「こころ」はそのまま歌わせると「こおろ」と発音されてしまいます。
しかしプログラムで勝手にキーオフを挿入するので気にせずとも大丈夫です。


「ん」に関しては本来は5種類もあり使い分けの方法は厳しくポケミク内蔵のワンチップVocaloidNSX-1仕様書P34にて定義されています。

http://yamaha-webmusic.github.io/nsx1-apps/specs/ANMW820A-001-10-j.pdf


しかし現状のこのプログラムではその使い分けがパーザ内部で定義されていないので「ん」(N)しか使えません。
(その内実装したいと思います。すみません :-)

複数の文字をまとめる

歌詞入力で文字を()ではさむと一音に複数の文字を載せることが可能になります。

例えば「(こん)にちは」は5音ではなく4音になります。
特に「ん」の文字は1つの音を割り当ててもらえない場合が多いので多用することになります。
しかし右隣が空いている場合には音を並べたほうが良い場合も多いので色々と試してみて下さい。

現在、既知のバグとして()でまとめた音が歌詞スロットの64文字境界をまたぐとバグります。(--;
申し訳ございません。その内、多分、直します。

()の中では()をネストして利用することはできません。
また次の{}による歌詞の予約も利用できません。

歌詞を予約する

ポケミクのハードウェアには歌詞スロットが16個用意されており、各スロットには64文字を記憶させることができます。
そのため1024文字が利用できるのですが、このプログラムでは直ぐに内容が壊れるインメモリスロットであるスロット0を使用するのを避けています。
このため最大では歌詞が960文字になります。


このままでは組曲のような大物が扱えないため、歌詞を予約という形で保存しておき後述のコマンドにて利用することが可能です。
ただし、歌詞のポケミクへの書き込みは1スロットで平均220[ms]の時間がかかり、最大では3.3秒強の時間の間、MIDIが利用できなくなります。
組曲では歌詞の無い間奏時間が十分にありましたが、制約の強い機能であることをご理解の上、ご利用下さい。


歌詞の予約は簡単で{}で囲まれた歌詞は再生ボタンを押した時点ではポケミクに書き込まれずPCのメモリに記憶されます。
{}で囲んで歌詞は出現順に番号が0から付けられ、後述のコマンドでポケミクに書き込むことが可能です。


{}の中では()を利用することは可能です。
{}の中で{}をネストして利用することはできません。

コマンド

コマンドは歌詞の中に記述します。
歌詞は1行毎に再生時にパースされますが@と#で始まる行はコマンド宣言と見做され歌詞には含まれません。

@は何本目の棒で実行するかを指定します。通常の楽譜のエディット時には何小節目であるかが数字で楽譜の上に表示されています。この小節番号は
1で始まりますが、@で指定する棒番号は0から始まります。従って小節番号から1を引き4(または3/4表記の場合には3)をかけた数字がその小節の棒番号になります。
棒番号はもちろん小節の中でも指定できます。


#はコマンドの開始を宣言します。コマンドはコマンド名と引数から成り立ち、コマンドと各引数の間は空白で区切らなければなりません


現在はコマンドには以下のものが存在します。

コマンド名 説明
ChangeSlot 正の整数を引数として1つ取り現在の歌詞スロットを指定されたスロットに移動させます。歌詞の位置はそのスロットの先頭になります
ChangeLyricPosition 正の整数を引数として1つ取り現在の歌詞スロット内部での位置を指定します。0から始まり63までとなります
WriteReservedLyrics 予約した歌詞をポケミクに書き込みます。歌詞の書き込み終了を確認しないため、歌詞の書き込みに十分な時間を考慮する必要があります
ChangeProgram 正の整数を2つ引数としてとります。第1引数にはチャンネルを、第2引数にはプログラムを指定します。GM源仕様書を確認して下さい
コマンドの使用例

@1424 #WriteReservedLyrics 0

棒番号1424にて予約された歌詞0番をポケミクに書き込みます。

#ChangeProgram 1 127

棒番号が指定されていない場合にはコマンドは再生開始時に全て実行されます。この場合には再生開始時にチャンネル"2"が127(拳銃の発砲音)に設定されます。
チャンネルの設定は0からの指定になることに注意して下さい。


英語

ポケミクは初音ミクv3と異なり英語の発音には対応しておりません。
NSX-1モードで子音だけが発音できるのかどうか試していないのですが、もし可能なら、プログラムをポケミクモードでなく、NSX-1互換モード対応に書き直せば少しはマシになるかもしれません。
現在のポケミクでは英語の歌詞を歌わせようとするとダサダサになるので注意が必要です。
一音毎にボリュームを設定すれば少しはマシになるかもしれませんが、、、、
ちょっと良くわからないので放棄です。


最後に英語の調教を投げ出した悪い例 (^^;


既知のバグ

  • テストプレイ時に歌詞の書き込みをpromiseで待っていないために歌詞の位置指定に失敗する場合があります。

妥協策としてShift+Tでなく、まずTを使用し、コンソールログを見ながら歌詞書き込みを待ち、歌詞書き込みが終了してから改めてShift+Tを開始することが挙げられます。
再生時にはこの問題は発声しません。

  • スロットの64文字境界で()による分割がまたぐと発声に失敗する

原因はわかっており、対策も取れるのですが、放ってあります(^^;
とりあえず音数を調節してそこで発声しないように回避が可能です。

  • スロットの変更に失敗する

プログラムは文字数を数えて自動でスロットを変更するため通常は問題がないのですが、原因不明で20回か30回に一回くらいの確率でスロットの変更に失敗する場合があります。
全く同じデータで成功する場合と失敗する場合があるためポケミク側の挙動の問題ではないかとも想像していますが、これだけは原因がわかりません。
今の所諦めるしかありません。

  • 再生後にミク音を曲の途中に置く場合、歌詞の発生が正しくない

これは原因もわかり対策も取れるのですが、テストプレイすれば正しい位置で再生できる(ただし、歌詞書き込みが行われる場合に上記のバグが発生する)のでまだ直してません。
その内、直します(!?)

  • 再生後にミク音を曲の途中に置く場合、歌詞の表示が正しくない。

これも上とほぼ同様です。

データ構造

楽譜のデータは全てCurScoreというグローバル変数に入っています。
楽譜の保存をSAVEボタンで行う場合にもこの変数をJSONにしてダウンロードさせているだけです。

CurScoreの重要な属性値について説明します。これを理解した上で使用すれば、デバッグコンソールから直接楽譜を編集することも可能になるため楽譜の小節毎のコピペ等をJavaScriptで行うことが可能になります。

notes

CurScore.notesは楽譜データそのものです。notesは配列の配列になり、各要素である配列は順に、棒0からの各音の設定を保存しています。


この配列要素内に保存される音は16bitのバイナリデータになります。下位ビットから説明します。

0-6
MIDIのキーの値です。マリオシーケンサのWAV音もMIDIのキー値として表現されていることが元のマリオシーケンサとは異なります。MIDIのキー値は1オクターブを12の音で表現し半音を含みますのでプログラムが半音であると認知すると♯で表記します。例えば61は楽譜上ではC#となります
7
キーが♭であるか指定します。1の場合♭です。例えば61は通常はC#ですがこのビットが1である場合にはD♭と楽譜上では表記されます
8-12
音の種類を0から31で指定します。0から14はマリオシーケンサのWAV音であり、15から31がMIDIのチャンネル0から15になります。0はミク専用で、9はドラムマップ専用です。
13,14
音の長さを指定します。0は無限長、1は長さ0でありキーオフ、2は長さ0.5です。MIDI音にしか使用しません。
15
予約。現在は使用しておりませんが、音長を8種類までに増やすか、別用途に使用するかもしれません。


音以外の要素として文字列で"tempo=nnnn"という文字列が入るとプログラムが動的に再生テンポを変更します。この仕様は将来コマンドに変更するかもしれません。
複数のファイルを画面の楽譜にDrag & Dropすることで1つにまとめることができますが、この時各ファイルのテンポがこの仕様により保存され、再生時に曲中でテンポを変更することを可能にしています。
このため再生時にユーザがGUIでテンポを変えることは可能ですが、再生位置がこのデータを含む棒に来た時に自動でテンポが変更される(値によっては元に戻る)場合が存在します。

tempo

CurScore.tempoは曲のテンポをBPMで指定します。GUI上のスライダでは10から1000が指定可能ですがデバッグコンソールからこの値を再生中に変更することであり得ない速さでの再生を楽しむことができます。
機械にしかできない最高に楽しい悪戯です。:-)

end

CurScore.endには楽譜のエンドマークの位置を指定します。最終小節の次の小節の頭を通常は指定します。

loop

曲の再生時にループするか否かはこの値です。trueかfalseを指定します。



謝辞

全ての音楽データはニコニコ動画で有名なPhenixさんが公開されているデータを改変させて頂きました。ありがとうございました。
http://music.geocities.jp/msq_phenix/

走るミクさんのドット絵は「ドット絵描こうZ」さんからお借りしました。
http://blog.livedoor.jp/dotez/archives/cat_10020030.html

両方とも著作権は全て作者様にあります。


マリオシーケンサ自体も某社様のものなので京都に向かって深々と御礼申し上げます。
勝手なことしてすみません。訴えないで下さい。m(_ _)m