A toy piano in my pocket

この週末に簡単なPianoアプリをiPhone用に作っていた。売り出すためというのではなく、最近移動が多く移動に時間を多く費やしている妻のニーズに応えようと思ったのだ。

もちろん、多くの有料・フリーを問わずそういったアプリがすでに数多くAppStoreに登録されているのは知っていたのだが、作ってみたくなったというのと、一度作って仕組みを持っておくと他の遊びにも使えそうという思惑もあった。

さて、わざわざ作るのだから、たとえば全部の鍵盤の音を減衰しきるまで録音しておいて、スイッチでそれを再生するだけというのは、確かにゴールには近道かもしれないけども、面白くない。やはり、Wave Tableを扱うオシレータをもったシンセサイザ、つまり簡単なサンプラーぐらいにはしたい。

iPhoneで音を扱うためのAPIは大きく分けてOpenALかAudio Unitになる。他にも簡単に扱えるようなものもあるのだけど、今回はローレベルで扱いたかったので除外した。

OpenALは以前テストしてみたことがあったのだけど、今ひとつiPhone上ではよくなかった。バッファアンダフローが頻繁に発生した。Call stackみてもAudio Unitにラッパーをしているみたいだし、オーバーヘッドを考えたらAudio Unitを扱うのが良さそうと言うところまでは分かっていたのだけど、iPhoneでAudio Unitっていうのも負荷的な問題は大丈夫なのかなといった疑問も少しあったのだ。

結論から言ってしまうと、Audio Unitの部分は意外とすんなりといってしまった。Undocumentedな部分で???となることが少なくなかったが、書かないといけないコードは拍子抜けするぐらいシンプルだった。AUGraphを作ってその中にアウトプットを追加し、ミキサーを追加し、ルーティングしてやるだけ。

もっとも、Appleのドキュメントでの手順ではどこからとも無くアウトプットがやってきていたので、アウトプットはデフォルトで何かしらあるのかと思ったら無いというような、少々はまった箇所はいくつかあったけれども。音を出すソフトのデバッグは音が出るまでが大変だ。

OpenALのテストの時の印象からするとAudio Unitはずいぶんと良い。5音ポリ(iPhoneのマルチタッチは最大5ポイントなのだ)ぐらいだとアンダーフローは発生していない。そのかわりUI側のタスクが若干引っ張られてるような感じを受けることがあるけど、音途切れが発生するよりは良い。

出力までの経路が完成したなら、あとはオシレータだ。当初は軽さだけを追求して、WaveTableからの読み出しもスキップのみしかさせていなかった。未だに乗除算は避けられるものなら避けたいと思ってしまう。浮動小数点も同様に。

オシレータだけだと寂しいので、ADSR付のエンベロープジェネレータも実装する。そもそもエンベロープジェネレータないとキーのオンオフどうするんだという話でもある。

すべてを実装して、試しに鳴らしてみる。なんだか、処理落ちもなく普通に音が出ている。もちろん、個々のオシレータなどは注意深く軽く実装したのだけど、所詮まだCレベル、実用にはまだまだ最適化が必要だと思ってただけに、またしても拍子抜け。

iPhoneのスピーカから出てくる音はおもちゃのキーボードのピアノの音みたいで、これはこれでかわいくて良いなと思ったのだけど、簡単なオーバーサンプリングをオシレータに追加してみると、やはり手を入れた分ぐらいは音がよくなる。それでもまだ処理落ちする気配がない。

こうなってくると欲が出始めて、つぎはローパスフィルタが欲しいとか思ってしまうのだった。