今年(2015年)の春くらいに、会社の後輩と一緒に作ったCloudnアプリコンテスト応募用のAndroidアプリのことを書こうと思う。
だいぶ今更感があるし、弊社の技術戦略なんかともまったく関係がないけれども、Advent Calendar用のネタが間に合わなかったせっかく頑張ったので、何か書いておくことにした。
※ソースコードはお見せできる状態でないので公開していません。すみません。
経緯
元は後輩2人がコンテストへの応募を企画していたところに僕が割り込み、3人チームになった。
何を作るか相談していたときに後輩のうち一人が「Androidで音声処理をしてみたい」と言ったことから、「Android端末で音を使って通信するアプリ」になった。
(他の案として「いびきを録音するアプリ」があったが、すでに存在したのと面白みを出しづらいので没になった。)
コンセプト
音で通信するアイデアはもちろんすでにあり、代表的なものに、電話で使われているDTMFがあるほか、最近では超音波を使った通信も実用されているらしい。
今回のアプリのアプローチはDTMFに近いが、DTMFよりも「聽いて楽しい」可聴音通信を目指してみた、というのがひとつのテーマとなっている。
作ったアプリ
Android専用アプリ。以下の機能を持つ。
- メッセージ文字列を「音」に変換し、Android端末のスピーカーから鳴らす
- 鳴らされた「音」をマイクで拾い、元のメッセージ文字列を復元する
- 作った「音」をWebで共有し、同じアプリを持っている人にメッセージを伝える
- 例: 文字列「ぬるぽ」を共有した例 http://a3os.com/melodian/share.html?melody_code=53,84,70
仕組み
メッセージを音にする
- 音階の「ドレミファソラシド」の8音のうちいくつかを同時に鳴らし、どれが鳴っているかの組み合わせで情報を表現する。
- 1信号(一度の発音)で8bitの情報を表現できる。
- 1信号で1文字を転送したかったので、文字の種類を平仮名(+一部の記号)に限定し、1文字を8bit以内で表現できるような対応表を作った。
- 例
- 「ぬ」→ ファ+ラ
- 「る」→ ド+ミ+ファ
- 「ぽ」→ ファ+ソ+ラ
- 例
- 1信号の長さは0.25秒とした。(これ以上短いと復元が不安定になる)
- つまり、1秒で4信号(=32bit)転送できる。転送効率は32bpsということになる。
音からメッセージを復元する
- マイクからの入力音をリアルタイムでFFTし続ける1
- 周波数の分布から「ドレミファソラシド」のそれぞれに対応する8つの周波数帯(たとえば、「ラ」なら440±5Hz)の音量を取り出し、閾値を超えていた音の組み合わせから文字を復元する。
- 例: ド+ミ+ファ → 「る」
- 上記のことを0.25秒おきに繰り返す。
音を共有する
- 「メッセージを音にする」部分をJavaScriptでも実装
- メッセージ全体をパラメータに含むURLを作成し、それを共有用WebページのJavaScriptで読み取って音を流す仕組み。
- Androidアプリにて上記のURLを生成してTwitterなどへの投稿を促すことで、あたかも音を共有しているように見える。
WebViewを使ったUI
AndroidアプリのUIは素直にJavaで書くかCordovaなどを使うのが一般的だと思うが、今回はこういう方法を試してみたくなり、
- アプリのUIはすべてHTML+JavaScriptで作って
assets
に入れ、WebViewで表示 - 音声処理部分は、Javaで実装し、
JavaScriptInterface
を使って必要に応じてUIから呼ぶ
という構成にした。(この判断のせいで、後で苦労することになった・・・。)
アプリの中で音をビジュアライズ(音に合わせてシャボン玉のようなものを表示)する部分があるが、ここはPaper.jsを使った。
振り返り
がんばった点:信号の切れ目の検出
- どこで文字が変わったかを検出するのが難しい。
- 同じ文字が連続すると同じ音が連続するので、「音が変わった」という判定基準は駄目。
- 結局、「一定時間同じ音が続いたら信号として検出」のようなロジックを地道に積み重ねることになった。
反省すべき点:WebViewの罠
UIにWebViewを使ったことで
- ローカルのURLに対してはクエリパラメータを渡せない
- Androidのマイナーバージョンによって挙動が異なる
- Canvasが遅い
などWebViewの面倒くさい仕様に振り回され、コンテスト申し込みの締め切り直前はデバッグ作業に追われることになってしまった上に、妙にモッサリとしたアプリになってしまった。
WebView+HTMLでUIのコーディング量が減ったかというと全然そんなことは無かったというかむしろJavaScriptとJavaを繋ぐブリッジを書く手間が増えたし、素直にJavaで書けばよかったと思う。
最後に
「音を合成/解析する」という方向は、アイデア次第で面白いことができそうなので、今後もたまに手を出していきたい。(音楽情報処理とか)
@社内外のエンジニア各位:
来年もまた似たようなことをするかもしれないので、興味を持たれた方はぜひお声かけください!