document

HTML5 & JavaScript side
twitter: @y_imaya

2013年05月

Web Audio API で Sound Font を使った標準 MIDI ファイルの再生

はじめに

Google Chrome では Web Audio API という API を使って音を鳴らすことができます。
今回、これをつかって Sound Font を使った標準 MIDI ファイル(以下 SMF と表記)のプレイヤーを作ってみました。
なお、仕様の具体的な話しなどはほとんどしません。
音楽的な知識などもほとんどないため、何かおかしなことをしていたらご指摘いただけるとありがたいです。

また、今回の実装はあくまでも実験・検証用のものなので実用にはまだ手を加えなくてはいけないところが多いため、もし利用としようと思う方がいたらそこは注意してください。
動作環境は PC 版の Google Chrome のみです。
現在開催中の Google I/O で Chrome for Android でのサポートも明言されたそうですので、そのうち Android でも利用可能になるかも知れません。

もう少し完成度たかめてから公開しようかとも思っていたのですが、旬の話題みたいなのでビッグウェーブに乗ろうと思います。

デモ

以下のページで公開しています。
サウンドフォント(約10MB)のダウンロードがあるのでロードが終わるまでは気長に待ってください。

構成

まず、大きく分けて SMF を扱う部分と Sound Font を扱う部分に分かれます。
そして、この二つの部分を WebMidiLink というものでつないています。

  • SMF
    • SMF の Parser
      • MFi(着メロで使われたファイルフォーマット。拡張子は mld ) から SMF への変換モジュールもあります
    • MIDI メッセージのスケジューリングを行い、WebMidiLink へメッセージを流すプレイヤー
  • Sound Font
    • Sound Font version 2 の Parser
    • WebMidiLink のメッセージを受け取り実際に音を鳴らすシンセサイザー

WebMidiLink とは g200kg さんの提唱している window の message イベントを利用した MIDI メッセージをやり取りする仕様です。
http://www.g200kg.com/en/docs/webmidilink/

Sound Font

Sound Font では音色毎にいくつかの音域に分けてサンプルが格納されています。
このサンプルにはキーが設定されているため、その音域はその指定されているキーをもとにして平均律で算出し、再生速度を調整することで鳴らす事が出来ます。
また、各サンプルには Loop Start, Loop End が設定されていて、Note On されている時はこのループをぐるぐる再生し続けます。
(この再生方法のため、Safari では loop の実装が整っていないためおかしく聞こえます)

Sound Font の再生では他に以下の機能に対応しています。(しているつもりです…。)

  • Attack, Decay, Sustain, Release
  • Pitch Bend 変更
  • ピッチの補正
    • coarseTune
    • fineTune
    • Pitch Correction
    • modEnvToPitch
  • Web Audio API で扱えないサンプリングレートの補正(22,050 未満を引き延ばす)

また、今回つくったものでは A320U という GPLv2 ライセンスで公開されている Sound Font を標準で利用するようにしています。

ループ

一部のゲームなどでは SMF 内で独自にループ再生の目印を付けていることがあります。
Ys2 Eternal で使われているメタイベントの Marker を用いたループや、PC 版 RPG ツクールシリーズなどで使われている CC#111 のループが有名です。
今回作成したプレイヤーでは上記の2つのループ方式にも対応しています。

音を鳴らす

SMF では 16 のチャンネルがあり、それぞれに Volume, Panpot, Pitch Bend などが設定できます。
今回作成したものでは、各ノートを以下のように接続して鳴らしています。

[BufferSource] ---> [Panner] ---> [Gain(Volume, ADSR)] ---+
[BufferSource] ---> [Panner] ---> [Gain(Volume, ADSR)] ---+---> [Gain(Master)] ---> [Destination]
[BufferSource] ---> [Panner] ---> [Gain(Volume, ADSR)] ---+

BufferSource は各ノートで、PannerGain(Volume, ADSR) でチャンネル毎の設定を適用しています。
そして最後はマスター・ボリュームである Gain(Master) に繋いでそれを出力するという感じになっています。

現在は割と大雑把に上記のような構成の AudioNode を NoteOn ごとに作って使い捨てているのですが、今後はこのあたりをもっと効率的に行う予定です。

謝辞

g200kg さんの Web Audio API の解説 にはかなりお世話になりました。
また、音がずれてるなどの指摘をくださった @miyazaqui さん、音楽関係の基礎知識などについて丁寧に説明してくださった @yoya さんのおかげでなんとか聞こえるレベルまでこぎ着ける事が出来ました。ありがとうございます。

その他

今回は minify したファイルのみの公開とします。
もったいぶってるわけではなくて、近いうちに大規模な書き直しを行おうと思っているからです。
minify されててもいいってひとは適当にデモからソースを読んでください。

また A320U を使用しているとき Cello の音がズレているという指摘をもらっています。
これはサウンドフォントの modEnvToPitch という設定を無視するようにすれば正しい音程になるようですが、これを無効にすると他の音でおかしくなるので現在調査中です。

音程がずれる問題の原因 (2013/06/03 16:15 追記)

@g200kg さんからご指摘をいただきまして、Modulation Envelope の適用を行ったら改善したようです。 modEnvToPitch では Attack でピッチ・ローパスフィルタの設定が最も変わるというだけで、この記事を公開した段階ではその実装のみを行っていたのですが、Envelope なので Attack と Sustain の差によってピッチの変化がずれ、その結果音程がずれていたものだと思います。

追記:2013/06/03 16:15

以下の場所でソースコードの公開を行いました。

zlib.js 0.1.6 をリリースしました

はじめに

本日 zlib.js 0.1.6 をリリースしました。ここでは告知とともに 0.1.6 の更新内容などを簡単に説明したいと思います。

なお、今回から Change Log を添付したあるので概要はそちらでも確認できます。
今回のバージョンでは主にビルド環境の整理やテスト・デバッグ効率の向上を行っています。

https://github.com/imaya/zlib.js

ビルド環境の更新、最適化

zlib.js は Closure Compiler でビルドしているのですが、今までは minify したコードに余分なコードが含まれている事がありました。
今回の更新では Closure Compiler の更新とその辺りの設定を見直す事で minify したファイルのサイズを縮小しました。
例えば、Inflate だけならば約 7KB とさらにコンパクトになっています。

Raw 形式、CRC-32 の独立ビルドの追加

今までは Deflate アルゴリズム単体でのビルドはなく、ZLIB や GZIP, PKZIP などのコンテナ形式への対応だけでしたが、今回からは Deflate のみの利用が可能になりました。

以下のようにして使います。

var plain = new Uint8Array(1024);

// compression
var compressed = new Zlib.RawDeflate(plain).compress();

// decompression
var decompressed = new Zlib.RawInflate(compressed).decompress();

また、CRC-32 アルゴリズムも単体で利用する事ができるようになりました。

var plain = new Uint8Array(1024);

// CRC-32
var crc32 = Zlib.CRC32.calc(plain);

Pretty Print ビルド、Source Maps のサポート

今回から開発・デバッグ用に Pretty Print されたバージョンのビルドと、Source Maps のサポートを追加しました。
Source Maps の使い方はドキュメントに書いてありますが、とりあえず使えるようにしただけなので使いにくいかも知れません。
もっと良い構成などありましたら教えていただけると助かります。

Travis CI のサポート

今回のバージョンから Travis CI をつかって Firefox, Chrome で自動テストを行うようにしました。
…とはいっても、時々テストに使っている BusterJS の実行がささってしまうのでイマイチな時もあります。

ストリーム展開のビルドを修正

zlib.js では inflate_stream.min.js というファイルで実験的にストリーム展開に対応しています。
前回のバージョンでは手違いでストリームに対応していないままになっていたのを修正を行いました。

記事検索
最新コメント
カテゴリ別アーカイブ
タグクラウド
QRコード
QRコード
  • ライブドアブログ