2009年10月22日木曜日

audioタグとJavaScriptでピアノを作ろうとしたけど諦めた

タイトル通りの話をざっくり書いてみます。
(下の方に(挫折した)サンプルあります)

HTML5といえば、canvasタグたーのしぃーひゃっほーいで、videoタグとaudioタグでリッチなコンテンツもFlashなしで見せてやんよ、って感じでございますが、canvasタグは個人的にも多少はいじってみて可能性を感じましたが、videoタグとaudioタグはこれといった「おもしろ方向性」が思いつかなかったので今まで全然いじりませんでした。が、ちょっと思いつきまして、audioタグ利用してブラウザだけで動作するピアノを作ってみようと、こんなことをやってみました。


まずピアノ単音をG1~C5まで用意します。wavファイルで2.5sくらいの長さの。
GarageBandでソフト音源トラックを作り、音色ピアノ、BPM120、4分音符1つ、をwavで書き出しました。GarageBandが最短小節数8という縛りがあるようで、無駄に長いwavが生成されたので、QuickTimeProで2.5sまで短くして、さらにもっと低レートにして、変換します。ってのをG1~C5まで42音あるのでそれだけの回数やりました。アホみたいです。


音ネタが用意できたら、html内にaudioタグを43個作ります。idに id_C3
とかつけて、それぞれのsrcに作ったwavを指定してloadします。ちなみに、制御はJavaScriptからのみ行うので、controller
= falseで、ページ内にPlayerっぽいものを表示しません。用意したwavは42個なのに43個のタグとは如何に?それはあとで書きます。


これで、鍵盤の絵描いて(各鍵盤は独立したobjで)、たとえばC3の鍵盤クリックしたら、onClick =
document.getElementById("id_C3").play(); で該当する音鳴らす、とかやればピアノができるなーと。

ただそれだけだとおもしろくないので、シーケンサーみたいに「再生」してみようかと。でも処理が複雑且つ重くなりそうなので、まずは和音は無視の方向で、単音でメロディーを奏でるだけのを考えます(って思考が先走ったので、鍵盤は作成してないです)。メロディを記述して、それを読み込んで順次タイミング合わせて音鳴らしていく、って感じですが、BASICやってた方はわかるでしょうか、PLAYコマンドの中身みたいな感じで書いて、それを解釈して再生ってのを考えました。

たとえば。「チューリップ」の冒頭(♪さいたーさいたーちゅーりっぷーのはーなーがー)なら、こんな感じに書くことにします。

C3.4,D3.4,E3.4,R.4,C3.4,D3.4,E3.4,R.4,G3.4,E3.4,D3.4,C3.4,D3.4,E3.4,D3.4,R.4

C3.4は、3オクターブ目のC、長さは4分音符、を意味する、というフォーマットを考えました。Rは休符です。

さてここで、42音用意したのに43個audioタグを作った意味を書いておきます。43個目は、42個のどれでもいいので名前変えてコピーしてloadしてobj.volume
= 0.0 にして、休符代わりに使います。
はい。

C3.4,D3.4,E3.4,R.4,C3.4,D3.4,E3.4,R.4,G3.4,E3.4,D3.4,C3.4,D3.4,E3.4,D3.4,R.4

これをどう読み込んで、解釈して、audioタグの制御に使うか、ですが、全部JavaScriptでやります。

http://なんららかんたら?BPM=120&Melody=C3.4,D3.4,E3.4,R.4,C3.4,D3.4,E3.4,R.4

という感じにサーバサイドでなんかやるっぽい記述なんかしてみちゃったりして。別にたいしたことやるわけじゃないのですが、document.locationでURI拾ってきて、?で分割、&で分割、"指定ワード="で対象を変数に読み込む、とやります。で、Melody=で取得した文字列は、さらにカンマで分割して配列に。この配列を順次再生していけばいいのです。


まず配列先頭には「C3.4」があります。ピリオドで分割して、キーと長さにわけて、

document.getElementById("id_C3").play();

で音を鳴らします。が、長さが問題。まあ自然減衰するピアノ音ってことで深く考えず、長さは次の音をいつ鳴らすか、にします。とすると、BPM120で4分音符の長さ、というのを計算すると、500msなので、配列の次の要素「D3.4」を500ms後に鳴らせばいい、ということに。

具体的には、今何個目の音(配列のデータ)を鳴らしているかを管理する変数を用意しておき、それをインクリメントして、

setTimeout(playNextSound, 500);

とかこんな感じで、500ms後に処理が実行されるようにします。これを配列が空になるまで、順繰りやっていけばいいのです。やったね!


…とうまくできたーと思ったのですが、まともなメロディを作ろうとすると、なんかうまく再生タイミングが合わなかったりします。
Safari4(audioタグサポートしてるはずだけど)では最初の音源の読み込みが激重かったり、同じキーを連続で鳴らすと汚かったり。Firefoxでもテンポ設定次第で、指定タイミングで発音されない音が出たり(Script自体は指定タイミングで走っているが、発音がずれる)、と、現実的なモノはできませんでした。あとiPhoneのSafariから使えるブラウザベースのをピアノシーケンサーを夢見ていたのですが、iPhone版Safariはもっとダメでした。audioタグを思うように制御できん。


一応サンプル。
Macの最新Safari、最新Firefox、Windowsなら最新Firefox、以外ではアクセスしない方がいいです。あとplayボタンが緑になるまで待って、クリックしてください。読み込みはMac Safariで10秒くらい。音量注意。(リンク先、MelodyのEditできそうな感じになってますが、いやできるのですが、動作不安定です)

audio tag sample(聞いたことある曲がサンプルとして読み込まれます)

audio tag sample (URL引数をPlay)(上に書いたようにチューリップをURLで渡してます)


やっぱこういう、音(とか映像)をぐりぐり用いるような遊びは、Flashやサーバサイドの力借りてやるべし、ってなところでしょうかね、今のところは。うーん、PC性能にもよるのかもしれませんが、PC性能が問題なくても、ブラウザの力の限界的なところかもしれません。僕のプログラミング能力が低いせいかもしれません。PrototypeやjQuery等のJavaScriptのLibraryまともにさわったことすらないもんでして…。

1 件のコメント:

匿名 さんのコメント...

はじめまして。
興味深く拝見させていただきました。
FF8でサンプルを再生しようとしたところ、読み込み時間が尋常ではなく再生できませんでした(笑)

WavをOggに代えれば多少速度を稼げるかもしれませんね。
そしてすべてのサンプルは読み込まずに、調整で限定する。
結果、転調や部分転調ができなくなってしまいますが、「とりあえず」ということで。

上手く行くかはわかりませんが、ちょっと挑戦してみようと思います。
ピアノのサンプル群を頂戴しました。
いつになるかはわかりませんが、上手く行きましたらご報告いたします。