はじめに
この記事は、セカンドライフ技術系 Advent Calendar 2018 - Adventar向けの記事です。
昨年のカレンダー*1でLibOpenMetaverseを中心に関連技術とともに音を扱ってみる - なんとなくとして、Windows環境で、SLでの音の形式とLibOpenMetaverseで音の取扱に触れ、さらにRadegastのメディア関連においてFMODをOpenALに書き換えてみるということまでをやってみたのを記事にしました。 達成できませんでしたが、当時目標としていたのは、「環境をXamarin.Androidとして、音を鳴らす」でした。 今年に入ってから、その続きをゆっくりと回収していましたが、やっとまとめることができました。 環境としてよりニッチなところになり、また内容も昨年のものとかぶる部分もありますが、お付き合いください。
概要
SLでオブジェクトに設定されている音は、LibOpenMetaverseを使用して、音データはOgg Vorbis形式で取得でき、また位置データ、音量データ、フラグ(ループか否か)も同時に取得できます。
取得したそれらのデータのうち、音データであるOgg Vorbis形式について、Xamarin.Androidでは、tremoloというlibogg/vorbisのモバイル向けのライブラリを使いデコードします。関連技術としてそのtremoloのビルドおよびバインディングについて触れます。
次にtremoloによってPCMにデコードされた音データを音量データ、位置データ等を使い、立体的な音を再生するために、 Xamarin.Androidでは、OpenTKでバインドされているOpenALというライブラリを使用します。関連技術としてそのOpenALのAndroid用のビルドについて触れます。 また、OpenALでの頭部伝達関数(HRTF)を使用した立体音響の設定について、Xamarin.Androidでの設定方法について触れます。また、OpenALで使用可能なカスタムHRTFの作成方法について触れます。
関連技術の準備ができたところで、具体的な実装として、昨年のカレンダーで実装したものを少々修正し、Androidに移植したRadegastの3DSceneと合わせて、どのようになるかを確かめてみたいと思います。また、HRTFを有効にした場合とそうでない場合を比較してみたいと思います。
なお、内容はまとめの形になり、過去に書いた記事を参照する形で進めていきます。
を上から順にあらかじめ読んでいただくと一連の流れとしてこの記事を読むことができると思います。
SLでの音の形式とLibOpenMetaverseで音の取扱
SLでの音の形式とLibOpenMetaverseで音の取扱については、Windows環境とAndroid環境で異なることはなく、昨年触れたとおりです*2。
SLのサーバには音はOgg Vorbisで16-bit, 44.1kHz monoで保存されています。
リンクした*3公式サイトはすでに無く、リンク切れしています。 現状、 http://libopenmetaverse.grimore.org/ において確認できるので、そこを参照してほしいです。
関連技術
Xamarin.Androidでのtremolo について
サーバから取得できる音データのOgg Vorbis形式について、Xamarin.Android(Android)では、tremoloというOgg Vorbisのモバイル向けライブラリを使いデコードします。
tremoloとその関連情報については、以下記事を参照してください。
tremolo or tremor -モバイル環境のOggVorbisのデコードについて- - なんとなく
tremoloのビルドについては、AndroidのバージョンがN未満であれば、システム部分にライブラリがあり、それを参照することができ、ビルドする必要はありませんでしたが、Nからは、独自でビルドし、アプリに組み込む必要があります。
ビルド環境がWindowsの場合は、CMakeでのNDKを使ったビルドが難しいので、Android.mk,Application.mkを作成しました。
また、CMakeでビルドするものとtremoloのC#向けのバインディングが存在しますので、それをforkし、Android.mk,Application.mkを加えたものを置いておきました。
GitHub - takeshich/tremolo-sharp at ndkbuild
tremoloのC#向けのバインディングについては、 リポジトリのtremolo-sharp.cs*4を見てください。
また、
Falplayer: infinite ogg vorbis player (alpha) - ものがたり
及び
でtremoloのC#向けのバインディングについて作者が触れている記事があります。
なお、当方はDisposeでov_clear()のところでエラーが出てしまうので、コメントアウトし、また、デスクトップ環境でも動くようにReadを少し修正しています。昨年のカレンダーで使用した、Ogg Vorbisのデコードライブラリはこちらに置き換えました。
Xamarin.AndroidでのOpenALについて
OpenALについては昨年の記事で触れているので、記事の対象部分を参照してください。
また、Xamarin.AndroidでのOpenALについては、以下の記事を参照してください。
Xamarin.AndroidでのOpenALについて - なんとなく
Xamarin.Android向けOpenALライブラリについては、HRTFを使用することを考慮すると自身でビルドし、アプリに組み込む必要があります。Xamarin.Androidで使用できるOpenALのライブラリのバイナリは、monogameのリポジトリに存在を確認でき、試用するのには適していると考えますが、若干古いため、自身でビルドすることをおすすめします。
Android向けOpenALライブラリのビルドに関するドキュメント*5は、古く、NDKを利用した方法についての記述が見当たらなかったので、NDKとCMakeを使ったビルドについて、環境構築からビルドまでのざっくりとしたものを書きましたので、以下の記事を参照してください。
HRTFについて
昨年の記事ではサラウンド(マルチチャンネル)にも触れましたが、モバイル環境では擬似的な立体音響でそれを実現することが可能です。OpenALでは頭部伝達関数(HRTF)を使用した擬似的な立体音響を使用することができます*6。OpenALでのHRTFを有効にする方法については、以下記事を参照してください。
HRTFは個々人で異なっているため、本来であれば計測したデータをもとに利用するべきなのですが、現実的には、公開されているデータで自分にあうものを探し、一番あっていると感じるものを使用するという利用方法になっています。そのカスタムHRTFファイルの作成方法については、以下記事を参照してください。
また、Xamarin.AndroidでのカスタムHRTFファイルの適用方法については以下を参照してください。
なお、iOSではシステム側にあるものは、OpenAL Softではないため、OpenALでHRTFを実現できません。Androidではそもそもシステムライブラリとしては、存在しないためOpenAL Softをビルドし、アプリに組み込む必要があります。iOSでHRTFを使用したい場合、別途OpenAL Softをビルドし、組み込む必要がありますが、使用するOpenALのライセンスがLGPLであるため、それに応じた検討をする必要があります。
Xamarin.AndroidでのFMODをOpenALに書き換えた実装について
さて、ここからは、具体的な実装を見ていきます。とはいっても昨年の記事でFMODをOpenALに書き換えたので、それを利用します。*7 C#でクロスプラットフォームでの利用を前提にしていますから、ロジック部分の修正は必要ありませんでした。 今回、 tremoloのところで触れましたが、Ogg vorbisをデコードするためのライブラリを変更したので、その変更に伴う修正を行いました。
- ライブラリの指定の変更
- ライブラリに沿ったデコードするデータの入出力の部分に若干の修正
以上の2点の修正を行いました。
Androidに移植したRadegastの3DSceneと合わせて動かすための実装も入れましたが、ロード時に起動するためのものを数行追加しました。3DSceneも音を鳴らす実装もどちらもRadegast由来ですので、特にハマることなく実装でき、動くことは確認できました。
確認できている問題点があって、ソースの数が256以上になる場合があり、OpenALでは対応していないという点で、実用的にするには、距離に応じて鳴らさないという対応を取る必要があります。公式Viewerでは同一Parcelで鳴らすなどの制限を行っているようです。
また、去年の記事で、座標について触れましたが、間違っています。なぜそうかいたのかは不明ですが、正しくは、
v.X = omvV.X; v.Y = omvV.Z; v.Z = -omvV.Y;
です。これは明らかに昨年に作ったバグですが、関連して、ListenOrientationも修正しました。 最終的な確認はマルチチャンネルの環境で行っていましたが、ListenPositionのアップデートについて、10度以上の回転かつ少々の移動が必要で、移動せずに回転しているとListenPositionが更新されず、正しく聞こえておりませんでした。
HRTFありなしの動画
HRTFを有効にした場合と有効にしない場合でどのような違いがあるのか比較してみたいと思いました。
OpenALでのHRTFを有効にする方法については、
device = Alc.OpenDevice(null); //context = Alc.CreateContext(device, (int[])null); int[] attrs = { 0x1992, 1,0 }; context = Alc.CreateContext(device, attrs); Alc.MakeContextCurrent(context);
とするだけですから、ON/OFF切り替えて、HRTFのありなしを比較できる動画を撮ってみました。 まだまだ3DSceneについては、バグを取り切れておらず、シェーダーもきちんと書いてないので、光の加減とかおかしいのですが、今回の目的は、実装した音を確認してみることですから、ご容赦ください。
HRTFは、 IRC1032を使用していますが、個々人で違いますのでHRTFを有効にしないほうが聞きやすいという方もいると思われます。HRTFのありなしでの違いが明確ではないと感じる方もいると思われます。
イヤホンもしくはヘッドホンを使用して、きいてください。
- キーボード音、HRTFなし、左後→右後→右前→左前→左後の順で
- キーボード音、HRTFあり、左後→右後→右前→左前→左後の順で
- 水戸黄門HRTFなし
- 水戸黄門HRTFあり
まとめ
Xamarin.Androidで、LibOpenMetavese,tremolo,OpenTK(OpenAL)を使って、SLのViewerの音周りの実装について順を追ってみてきました。 個人的には、職人さんが目的のものを作るときに専用の道具がないから道具から作り始める的なイメージでこの作業をしていました。
去年のカレンダーで目標としていたことを回収するべく、今年いろいろと手を動かしてみましたが、その道のりはそれなりに時間の掛かるものでした。 どう考えても去年のカレンダーの記事では収まらない容量と内容でした。
手を動かしていく中で、きつかったのは、 Xamarin.Androidの10月のアップデートで Xamarin.Androidのバグ*8によって、LibOpenMetaverseのHTTPのGETが非同期でうまく動かない状態でしたので、ダウングレードして、動くようにしましたが、ダウングレード方法についてドキュメントがなく、強引な試行錯誤をする必要がありました。
また、今回Androidの動画を撮ってみましたが、Androidでは画面の映像については簡単に録画できるのですが、Androidの音声部分については簡単にはいかないものでした。
Viewerの内部ではこんな感じで実装されているよというのが伝わればなぁと思っています。
今更なのですが、LibOpenMetaverseのXamarin.Android向けのビルド方法とかまだ書いていませんでした。たぶん、そのうち書きます。魔改造して速度が出なくなってしまった部分もあるので、それもそのうち改修します。やろうやろうと思ってもう数年なんですけど。ソースは公開してあったりします*9。
今後、Androidに移植したRadegastの3DSceneがまぁ動くよねという感じになったら、Xamarin.iOSで動くかやってみたいところですが、ゆっくりコツコツとやっていこうと思います。
おまけ
HRTFの効果はよく分かるけど、とてもノイジーなので。。。
- どこかの海岸HRTFなし
- どこかの海岸HRTFあり
*1:https://adventar.org/calendars/2479
*2:http://takeshich.hatenablog.com/entry/2017/12/22/000000?id=SL%E3%81%A7%E3%81%AE%E9%9F%B3%E3%81%AE%E5%BD%A2%E5%BC%8F#SL%E3%81%A7%E3%81%AE%E9%9F%B3%E3%81%AE%E5%BD%A2%E5%BC%8F
*3:http://takeshich.hatenablog.com/entry/2017/12/22/000000?id=SL%E3%81%A7%E3%81%AE%E9%9F%B3%E3%81%AE%E5%BD%A2%E5%BC%8F#LibOpenMetaverse%E3%81%A7%E9%9F%B3%E3%81%AE%E5%8F%96%E6%89%B1
*4:https://github.com/takeshich/tremolo-sharp/blob/ndkbuild/tremolo-sharp/tremolo-sharp.cs
*5:https://github.com/kcat/openal-soft/blob/master/XCompile-Android.txt
*7:https://gist.github.com/takeshich/6e071562bf5536619a378b9f0c0b6c1f
*8:https://github.com/xamarin/xamarin-android/issues/2297
*9:https://github.com/takeshich/libopenmetaverse/tree/forandroid/