WebAudioAPIとWebWorkerとの通信オーバーヘッド

Web Music Developers JPのアドベントカレンダー 21日目の記事です。

今日はWebAudioAPIのオーディオスレッドからWebWorkerを呼び出す際のオーバーヘッドについて書きます。ここでの内容はChrome限定で、Safariでは話が違ってきます。

オーディオスレッドとは?

まずは以下の実験を見てください。音量は絞ってますが音がなるのでご注意を。

  • startでカウンターが回り始めます。
  • alertでアラートダイアログが表示されます。

ここでおかしなことが起こります。ダイアログが表示されている間も音が鳴り、カウンターが回り続けてるんですね。Javascriptはシングルスレッドです。スレッドがダイアログによりブロックされているのに、いったい誰がカウンターを回しているのでしょう?

答えはオーディオスレッドです。具体的には下のようになっています。

var ctx = new webkitAudioContext(); 
var node = ctx.createJavaScriptNode(2048, 1, 1); 
node.onaudioprocess = function(){ 
  // この中がオーディオスレッド
}

WebAudioAPIにはこういう特殊なプロセスが存在しているようです。内部の実装が気になるところですね。ちなみにSafariではsetInterval()などでカウンターを回したときと同じような動作になります。

オーディオスレッドからのWebWorker呼び出し

では本題のオーディオスレッドとworkerの通信オーバーヘッドについて実験で計測してみましょう。

ここでの動作は上から順番に以下の通りです。

  1. onaudioprocess内で正弦波を合成する
  2. onaudioprocessからworkerにpostMessage()後、addEventListener()により正弦波を受け取る
  3. 2. のonaudioprocessの変わりにsetInterval()を使う

Chromeで実験するとレイテンシが3<1<2となります。つまりオーディオスレッドからworkerを呼ぶと、普通にworkerを呼ぶよりパフォーマンスが悪いようです。

WebAudioAPI仕様に関する議論

何でこんなことになるのでしょうか?興味深い議論をW3Cのメーリングリストで見つけたので抄訳して紹介しておきます。

a sample to use Web Worker with current JavaScriptAudioNode

JavaScriptAudioNodeとworkerを使ったサンプルを作ってみたよ。

Re: a sample to use Web Worker with current JavaScriptAudioNod

そのやり方は現状正しい。しかし良いパフォーマンスを得ようとすればworkerスレッドからコールバック関数(*1)を直接コールできるようにする必要がある。ここでの問題はメインスレッドからworkerスレッドを呼ぶという余計なスレッドホップにより遅延が発生してしまうことだ。

(*1) onaudioprocessに設定したコールバック関数

つまり現状はworkerスレッドからオーディオスレッドを呼べないのでパフォーマンス的にちょっとなぁ、と言うわけです。

それを実現しようという話もありました。

Re: Web Audio API and Web Workers

コールバック関数の代わりに、createJavaScriptProcessingNode関数にWeb Workerを食わせてみてはどうだろうか?

これはシンプルな解ですね。これが出来るようになると目の前がパーッと明るくなりそうです。

正直WebAudioはまだ実用の域に達していないと感じさせられる部分が多いのですが、その中でハックしていくのがWebAudioの面白みでもあると思います。何はともあれ今後の議論が楽しみですね。

以上、オーディオスレッドとWebWorkerにおける通信オーバーヘッドについてでした。同じWebkitでもSafariだと違う結果が得られるので、興味があれば試してみてください。

Web楽器のプラグイン規格を作りました

Web Music Developers JPのアドベントカレンダー 13日目の記事です。

現在Web上でDAWを作ろうとするといくつか壁がありますが、まずはVSTのような規格が欲しいですよね。そこで試しにWeb楽器向けのプラグイン規格をつくってWebWorker上で動かしてみたらうまくいったので、規格の仕様と実験ページを公開します。

規格はAudio Script Interface(以下ASI)と呼ぶことにして話を進めます。

どんな規格?

ASIはJavascript上ならどこでも動作すること、ホスト側でGUIやAudioAPIのインターフェースを吸収することを目的としています。今回はWebWorker上で動作させていますが、ASI規格自体はWebWorkerのインターフェースに依存させないように配慮しています。

規格があると

  • 開発者は音源を合成するエンジンの開発に集中できる
  • 利用者はASI規格の楽器を組み合わせて使うことが容易になる
  • Javascriptが動けばどこでも動かせる

などなど便利なことが多々あります。将来的にはnode.js上で音源データを作成したり、Windows8のMetroアプリ上で楽器の演奏がたらいいなぁ、とか思っています。

逆にデメリットはというと、

  • 思い通りのGUIコントロールパネルが作れない
  • ホストがないと動かない

そのうち問題はいっぱい出てくると思いますが、今はこんなところでしょうか。

実験サイト

規格考えましたっていうだけじゃ面白くないので、ConnectSynth という実験サイトを作りました。Web Audio API対応ブラウザ(ChromeSafari)を推奨しますが、Audio Data APIにも対応してあります。

何はともあれデモです。ソースコードこちら

このサイトで以下のようなことが出来ます。

  • プラグインのファイルをサーバーにアップして起動
  • プリセットの保存
  • ソースコードの編集
  • 人が公開したプラグインを自分の作業領域にコピーして編集(Fork)
  • WebMidiLink に対応

Twitterアカウントでログインすれば、テストや公開も可能なので良かったら試してみてください。仕様はサイトにまとめてあります。ただし説明書がありません(汗)。なんとなく使えると思いますが、、作っておきます。

プラグインがどうやって動作しているのかも簡単にまとめました。

  • ホストの動作
    • プラグインをWebWorker上でimportScripts(“main.js”)する
    • プラグインから取得したGUI情報を元にコントロールパネルを描画する
    • ボタンが押されたりしたら、プラグインにそのイベントを送信する
    • キーボードを描画し、MIDIイベントをプラグインに送信
    • プラグインから取得した音波形データをAudioAPIに送って音を鳴らす
  • プラグインの動作
    • ボタンの配置情報などをホストに送信
    • MIDIイベントの受信
    • ボタンなどを通じてパラメータの変更イベントの受信
    • 音波形データ(Float32Array)をホストに送信

今後

ConnectSynthはHeroku + S3で作ったんですが、米東海岸にサーバーがあるので結構遅いです。あんまり不便なようなら国内サーバーに移すか、ローカルでASIプラグインをデバグできるSDKみたいなものを作るかします。エフェクトの仕様やMIDIと連携させたシーケンサー辺りまで手が広がれば、ちょっとした打ち込み曲が作れるようになりそうでワクワクしますね。

現状は概念を説明するのに必要な仕様しかありません。ご指摘や要望、アドバイスなどがあれば是非WebMusicDevelopersJP に参加してポストしてみてください。では。

staging環境のDjangoアプリをBasic認証でブロックする方法

herokuにdjangoでstaging環境作ろうとしたときに、Basic認証でブロックしたかったのでMiddlewareを作成しました。

Gist

どなたでも自由に使ってください。使い方はsettings.pyに以下の用に追加すればOKです。

  • settings.py
STAGING_HTTP_BASIC_AUTH_ID = “id” 
STAGING_HTTP_BASIC_AUTH_PW = “password” 
MIDDLEWARE_CLASSES += (
  “staging.AuthMiddleware”,
)

javascriptでwavファイルを作って鳴らす

jsではバイナリファイルの読み書きが出来ます。その機能を使うことでjslinuxGameBoyOnline みたいな一昔前ではありえなかったwebアプリが誕生しています。

そこで勉強がてら、jsでwavファイルを作って音を鳴らしてみました。動作はChromeFirefoxで確認しています。たぶんsafariも大丈夫のはず。

Gistに張っておきます。あとためしにjsdo.itも使ってみました。

wavファイル以外にもMIDIやmp3なんでもjsで作ることが出来るので夢が広がりますね。

git-flowのgitコマンドを確認する方法

職場でgit-flowを使うことになったので、git-flowコマンドの際に投げられるgitコマンドを把握するためソースコードを眺めてました。

gitflow

gitflow-commonファイルに以下の様な記述があり、show_commandsフラグを立てれば標準エラー出力にgitコマンドが出力されます。

  • gitflow-common
git_do() {
  # equivalent to git, used to indicate actions that make modifications
  if flag show_commands; then
    echo "git $@" >&2
  fi
  git "$@"
}

このフラグはgit-flowファイルのmain関数内で設定されており、以下のように変更すれば常にgitコマンドが表示されるようになります。編集するファイルはぼくの環境では/usr/local/bin/git-flowでした。

  • git-flow
#DEFINE_boolean show_commands false ’show actions taken (git commands)’ g
DEFINE_boolean show_commands true ’show actions taken (git commands)’ g

オプション引数が使えない場合がある

上のソースコードを見るとgオプションをつければいいじゃないかと思ったのですが、動作がおかしくなる場合がありコードを追ってみました。結論だけ言うとgit-flowのバグによりオプション引数が有効にならない場合があります。すでにpull requestがあがっていたので参照しておきます。

https://github.com/nvie/gitflow/pull/232

従って以下のコマンドでオプション引数を使うことはこのsubmitが承認されるまでは控えたほうがよさそうです。(既に4ヶ月放置されているようですが、、)

  • git flow feature start
  • git flow hotfix start
  • git flow release start

そのためデフォルトでshow_commandsをtrueにするやり方を採用することにしました。