マリオシーケンサをWebアプリにしてみた

(6月11日 加筆)

マリオシーケンサChrome Experimentsに採用されました!
https://experiments.withgoogle.com/chrome/mario-sequencer

  • 画面サイズの変更を可能に。左上のセレクタを使用して下さい。
  • 赤い線の箱でガイドを表示
  • マウス位置と音符が置かれる位置が微妙にズレていたのを修正


(5月29日、加筆修正)
現在、HTML5や携帯等のプログラマのお仕事を探しております。
よろしくお願いいたします。



ニコニコ動画で一世を風靡したマリオシーケンサを覚えていらっしゃる方は多いのではないでしょうか?
HTML5Canvas、Web Audio API等を勉強するついでにWebアプリにしてみました。

こちらで遊べます。現在、手抜きでChromeでしか動きません。
http://minghai.github.io/MarioSequencer/


ソースはこちら。
https://github.com/minghai/MarioSequencer

リモートからのダウンロード時にPROMISEで待つように作り直しました。
Downloadを表示するspinner(くるくる回るやつ)が消えれば準備OKです。

本家のマリオシーケンサに比べ、音の3音制限が無く、シフトとコントロールのクリックで♯と♭の半音が使えます。

前バージョンに追加して消しゴムも実装しました。
Undoが実装されていないので下のほうの組み込みの曲を押すと入力中の曲が全て消えますのでご注意下さい。


画面下にダウンロードのボタンがありますが、これを押すと作曲中のデータを保存することができます。保存したデータはDrag & Dropで画面に入れると反映されます。


Drag & Dropには本Webアプリのデータだけでなく、本家のMario SequencerのデータファイルであるMSQファイルが使用可能です。(拡張子msq)

ニコニコ動画でMario Sequencerでの大作をいくつも作られたPhenixさんがそのデータの一部を公開して下さっています。

http://music.geocities.jp/msq_phenix/

ここからデータを落として展開し、全て選択してドラッグアンドドロップすることで連続して聞くことが可能です。
ただし、一部のファイルには番号が1.5.5.5.5のように付いているものがありますが、これらは
通常の小数1.5555になるように直して下さい。また番号の付いていないファイルなどは連番を付ける必要があります。

複数ファイルを接続する場合に、異なるテンポの曲を繋げると両方のテンポを記憶し、曲の途中でテンポを変更することができます。
複数ファイルを接続することで24小節制限が無くなり、理論的にはほぼ無制限の長さの曲が作曲できます。

MSQもJSONもDrag and Dropを行うと1つのファイルに接続することが可能です。
Phenix氏が作成された組曲ニコニコ動画を試しに上げてみました。

http://minghai.github.io/MarioSequencer/?url=NikoNiko_suite.json&auto=true

URLにCGIのGETの要領でオプションを指定することができます。
作成されたファイルをどこかにアップロードし、URLで指定すれば好きなファイルを外部からロードできます。auto=true(またはa=t)を付けることでロード後に即演奏を開始します。

以上、楽しんで下さい。
ここから下は開発者以外、読む必要がありません。

HTML5上でのゲーム開発について

HTML5上でのゲームの開発はCanvasに描画して、requestAnimationFrameで回すというのが王道かと思います。正直、ゲーム開発には低レベル過ぎてあまり効率の良いものではない気がします。
既にHTML5上でゲームを開発するフレームワークがいくつも存在するので、そういったものを利用したほうが良いのかもしれません。今回は使用していません。

JavaScriptのパフォーマンスはかなり良く開発初期には音楽という特性に全く間に合わないのではと考えていたのですが、自分の一世代古いCorei7のマシンでもBPM1500くらいが余裕で回せました。しかし、2008年のMacBookで実行した所かなり重く感じました。ChromeMac OS版ではなぜか2008年のMacBookにてGPUを使用することができないのでCPUがぶん回ります。GPUによる二次元レンダリングのハードウェアアクセラレートが使用できないと重いようです。

開発で一番残念だったのはやはりブラウザ間の差異です。PREFIXが相変わらず必要なだけでなく、疑似要素、pseudo elementの名前等には各ブラウザで全く統一性が感じられません。
今回はChromeだけ対応にしてしまいましたが、ある程度完成したらFirefoxだけには対応したいと考えています。

今回、Web Audio APIを使用しましたが、かなり使いやすかったです。こちらでO'reillyの本が全文公開されています。
http://chimera.labs.oreilly.com/books/1234000001552

今回のアプリでは各音の種類にはFの音しか用意されていなく、playbackRateを操作することで音程を操作しています。この課程で本家には無かった半音の演奏も可能になっています。
残念だったのはcreateBufferSourceで作成したノードにはdetuneが無いことです。音程の操作をかなり簡単にするメソッドなのですが、どうも自ら作成した音のソースにしか無いようです
http://chimera.labs.oreilly.com/books/1234000001552/ch04.html#s04_2

Web Audio APIの再生はいくらでも重ねることができるのですが、マリオシーケンサは同じ音が続くと音がキャンセルされます。この部分をわざわざ実装しなければならないのが大変でした。
マリオシーケンサの音のキャンセルルールが明文化されていないので適当に実装してあります。このような細かな部分が元祖から本家に至るまで細かく異なるようです。(それを言えば元祖は音色によっては複数のサンプリングを異なる音程に用意しているようですが).

再生の音の重ね合わせが多いとひずみが発生します。これを抑えるためにcompressorが入れてあったのですが、compressorを入れるとプチプチノイズが目立つようになり取ってしまいました。
prototype.playChordにコメントアウトして残っていますのでコンプレッサが欲しい人はそこをいじって下さい。ローパスフィルタを入れようかと思ったのですが時間が足りずに入れていません。

マリオによる音楽の再生はrequestAnimationFrameが60FPSであるという前提を利用しています。しかしベストエフォートであるこのAPIは測ってみても5msかそれ以上は振れがあるようです。最初は演奏開始時から経過時間を測定し、綿密に演奏するアルゴリズムにしていたのですが、それだと再生中にテンポを変えることができず面白くありませんでした。現在は現在位置と現在のテンポの値から簡易的に音を鳴らすタイミングを量るようにしています。結構いい加減なのですが、安定して再生できています。

60FPSで再生するなら理論的には3600BPMまでは行けそうな気がしたのですが、実際には2000BPM近くから音の予約と実際の再生の間のラグが大きくなっているような気がします。画面と音との同期が崩れてしまいました。Chromeの開発者コンソールを開いて再生中に以下の要領でGUIでは設定不可能なテンポを設定することができます。

curScore.tempo = 2000

今回、初めてDrag & DropAPIを使ってみましたが、余計な権限もいらず簡単でとても良いですね。

またPromiseも多用してみました。
Promiseは複数ファイルを並列にダウンロードしつつ、ファイルを番号順に処理するのを並列に行うという技をこちらで紹介していたので参考にさせて頂きました。

http://www.html5rocks.com/en/tutorials/es6/promises/

この記事は余計なことも多く書いてあって長いのが難点ですが、色々なケースに対応する手段がサンプルが豊富に紹介されていてとてもお勧めです。

今後の予定

やりたいネタは数多くあるのですが、勉強しなければいけないことも多過ぎて困ったものです。
とりあえずイメージと音は除いて、コードは一切の権利を主張しませんので皆でフォークして拡張し下さると嬉しいです。