なんとなく

誰得感満載な記事が多いかも。Mono関係とLinuxのサーバ関係、レビューとか。

Xamarin.AndroidでのOpenALについて

はじめに

takeshich.hatenablog.com

の続き。

上記記事では、Windows上での

  • OggVorbis(Vorbis->PCM)のデコード
  • OpenAL
  • RadegastにおいてFMODで実装されているものをOpenALでの実装に変更

について記述している。

対して、その流れでAndroid(Xamarin.Androidで使うことを前提としている)で

  • OggVorbis(Vorbis->PCM)のデコード
  • OpenAL
  • Radegast においてFMODで実装されているものをOpenALでの実装したものをXamarin.Android向けに移植*1

について、順に見ている段階で、

前回は、

takeshich.hatenablog.com

で、Xamarin.Androidでの

  • OggVorbis(Vorbis->PCM)のデコード
    • ライブラリの調査
    • Androidでの検証

について見た。

今回は、Xamarin.Androidでの

  • OpenAL
    • OpenALAndroid用のビルド
    • HRTFを有効にした際にOpenALにビルトインされているHRTFの定義ファイルで再生

について見ていく。

なお、次回は、

takeshich.hatenablog.com

  • HRTFの定義ファイルの作成
  • HRTFの定義ファイルの適用

自分と使用しているイヤフォンにあっていると思われるHRTFの定義ファイルを作成し、それを適用し、再生できるまでを目標とする。

Xamarin.AndroidにおけるOpenAL

OpenALとは?

Wikipediaより引用すると

OpenAL (Open Audio Library)はクロスプラットフォームのオーディオAPIであるフリーソフトです。マルチチャンネル3次元定位オーディオを効率よく表現するように設計されました。 OpenAL - Wikipedia

で、OpenGLで描画して、そのオブジェクトから出る音はOpenALで3Dので表現をするという感じで、OpenGLに似せて作ってある。

さて、このOpenAL、この記事で扱うライブラリを明確にするため、派生を見る必要がある。

OpenALのリビジョングラフ
OpenALのリビジョングラフ

ライセンスについて最初はオープンソースだったものの、途中で、プロプライエタリになったため、プロプライエタリになる直前をforkしたOpenAL Softというものが存在する。オープンソースライセンスで使用する場合は、OpenALの1.1かOpenAL Softのものを使用するのがほとんどのようだ。

この記事では、OpenAL Softについてみていく。

OpenAL Soft

OpenAL Soft - Software 3D Audio

OpenALの代替としてOpenAL Softは普及している。OSSのコンテキストでOpenALというと現在ではOpenAL Softについて言っているように感じる。 ライセンスはLGPLで、バイナリ配布時にライブラリ自体を改変していなければ、DLLでの組み込みとなるため、アプリケーションのソースの公開をする必要はない。

なお、OpenAL SoftはOpenAL 1.1と比較して、

http://openal-soft.org/openal-extensions/

にあるような拡張がなされている。

OpenTKにバインドされるOpenAL

C#OpenALを扱う場合、OpenGLやOpenGLES、そしてOpenALバインディングしているOpenTKが利用されることが多い。 OpenTKは、Xamarin.AndroidやXamarin.iOSでも利用できる。

OpenTKでバインドされているOpenALは、そのソースのヘッダに

/* AlcFunctions.cs
 * C header: \OpenAL 1.1 SDK\include\Alc.h
 * Spec: http://www.openal.org/openal_webstf/specs/OpenAL11Specification.pdf
 * Copyright (c) 2008 Christoph Brandtner and Stefanos Apostolopoulos
 * See license.txt for license details
 * http://www.OpenTK.net */

https://github.com/opentk/opentk/blob/develop/src/OpenTK/Audio/OpenAL/Alc/Alc.cs

とあるようにOpenAL 1.1を対象にしたバインディングのようだ。 そのため、OpenAL Softで拡張されたものは対象ではなく、バインディングされていない。 OpenAL Softで拡張された関数を使用する場合は、別途追加でバインディングしたクラスを用意する必要がある。

今回扱うものはお試し程度であったため、既存関数の引数を拡張対応したもので直打ちしたが、通常運用する場合は、バインディングしたクラスを使ったほうが良いだろう。

Windowsで書いた同様のものがXamarin.Androidで動くか試してみる

まず、とりあえず、Xamarin.AndroidOpenALが動くか、様々なもので使われているから動くのは当たり前であるが、Windowsで書いたソースで動くのかを確かめてみた。

LibOpenMetaverseを中心に関連技術とともに音を扱ってみる - なんとなく

において触れたものをXamarin.Androidでも同様に実装してみた。

ライブラリについては、Android向けのビルドされているものを探したところ、monogameのもの*2が見つかった。

しかし、versionが1.16.0と若干古い。

今回はHRTFに対応させてみるというのを目的としていたので、動作確認には使える。HRTFに対応しているものの、最新版ではHRTFの定義ファイルがビルトインされており、HRTFを簡単に試すことができる。

ただ、そのバイナリを使用するためには、ビルドする必要があり、ビルド環境の構築をするなど手間がかかる。

とりあえず、Windowsで書いたのがXamarin.Androidで動くよねというところを簡単に確認してみたかったので、monogameで使われているものを一時的に使用した。

念の為書いておくが、Xamarin.AndroidOpenALを使用するには、プロジェクトのlibsにビルドアクションをAndroidNativeLibraryにしたlibopenal32.soを配置する必要がある。

f:id:takeshich:20190114170320p:plain

試してみるとどうもエミュレータだとエラーが出て音が出ない。当初エミュレータでのみ確認していたから、ハマった。

ただ、実機では動いた。

いろいろと調べてみたら、2016年頃に修正*3されており、使用していたエミュレータのOS VersionがMで、エミュレータを最新(O)にしたら問題なく動いた。

実機はOであったから、もしかすると実機のMだとエラーが出るのかもしれないが、検証環境がない。

tremolo or tremor -モバイル環境のOggVorbisのデコードについて- - なんとなく

で実装したOgg Vorbisも加えて、動くことを確認できた。

Android用のOpenALのビルド環境構築

OpenTKでバインドされているOpenALが動くことが確認できたので、次は、最新版で動くかということを確かめるために、ビルド環境を整えるところから始めた。 GitHubリポジトリ*4を見るとクロスビルドでAndroidでもビルドできるようなことが書いてあるので、DockerでLinux上でNDKをインストールし、CMakeが使えるように整えた。

Hyper-Vを起動した状態で、Androidエミュレータも動くようになったので、再起動をせずに使え、とても楽になった。

LinuxAndroid SDK,NDKをインストールした後について書いていく。dockerfileは、

https://github.com/lakoo/lakoo-android-ndk

を利用した。

linux(今回はubuntu)側でもsudoの設定をして、

sudo apt-get install vim make cmake 

をインストールした。

なお、makehrtfをネイティブな環境でmakeする関係で、

sudo apt-get install build-essential

も必要だった。

CMake

リポジトリには、

https://github.com/kcat/openal-soft/blob/master/XCompile-Android.txt

という物があり、中身を見てみたが、クロスコンパイル用で、NDKでビルドするのとは、ちょっと違うようだった。

はて~と困ったが、

https://github.com/takeshich/openal-soft/blob/master/.travis.yml

が見えたので、自動ビルドしているんだろうから、おそらくAndroidのビルドあるだろうと思い、中身をみてみてた。

cmake \
  -DCMAKE_TOOLCHAIN_FILE=~/android-ndk-r15/build/cmake/android.toolchain.cmake \
  -DALSOFT_REQUIRE_OPENSL=ON \
  -DALSOFT_EMBED_HRTF_DATA=YES \
  .

openal-soft/.travis.yml at master · takeshich/openal-soft · GitHub

あった。

これを参考にして、

cmake -DALSOFT_REQUIRE_OPENSL=ON -DALSOFT_EMBED_HRTF_DATA=YES -DCMAKE_TOOLCHAIN_FILE=/opt/android-ndk-linux/build/cmake/android.toolchain.cmake -DANDROID_NDK=/opt/android-ndk-linux -DCMAKE_BUILD_TYPE=Debug .

で行けた。

なお、CPUごとに作成する必要があったので、以下内容のMakefileを作成した。なお、これ*5を利用した。

ANDROID_NDK_HOME = /opt/android-ndk-linux
ANDROID_ABIS = armeabi-v7a arm64-v8a x86 x86_64

all:
        for abi in $(ANDROID_ABIS); do \
                mkdir -p build/$$abi ; \
                cd build/$$abi ; \
                cmake -DALSOFT_REQUIRE_OPENSL=ON -DALSOFT_EMBED_HRTF_DATA=YES -DCMAKE_TOOLCHAIN_FILE=$(ANDROID_NDK_HOME)/build/cmake/android.toolchain.cmake -DANDROID_NDK=$(ANDROID_NDK_HOME) -DCMAKE_BUILD_TYPE=Debug -DANDROID_ABI=$$abi ../../ ; \
                make ; \
                cd ../.. ; \
        done

clean:
        for abi in $(ANDROID_ABIS); do \
                cd build/$$abi ; \
                make clean ; \
                cd ../.. ; \
                rm -rf build/$$abi ; \
        done

OpenTKでOpenALのHRTFを有効にする

OpenTKでOpenALのHRTFを有効にする方法については、

HRTF mixing

An application may request mixing using a Head-Related Transfer Function,
or HRTF. HRTF can provide better spatial acuity when using headphones by
using special filters designed to replicate how sounds are affected by the
shape of the listener's head as they come in from a given direction. To
request HRTF, specify the ALC_HRTF_SOFT attribute to alcCreateContext or
alcResetDeviceSOFT with the value ALC_TRUE.

ALCint attrs[] = {
    ALC_HRTF_SOFT, ALC_TRUE, /* request HRTF */
    0 /* end of list */
};
context = alcCreateContext(device, attrs);

http://openal-soft.org/openal-extensions/SOFT_HRTF.txt

とあり、

#define ALC_HRTF_SOFT                            0x1992

openal-soft/alext.h at master · kcat/openal-soft · GitHub

と定義されているから、OpenTKでは、

  device = Alc.OpenDevice(null);
  //context = Alc.CreateContext(device, (int[])null);
  int[] attrs = { 0x1992, 1,0 };
  context = Alc.CreateContext(device, attrs);
  Alc.MakeContextCurrent(context);

としてあげればよい。

あくまでも、これは試行のためであって、実際に普段遣いする際には、alcResetDeviceSOFTも使うことも想定される。 そのため、再度の記述になるがOpenTKでの実装は、OpenAL Softで拡張されたものは対象ではなく、バインディングされていない。 OpenAL Softで拡張されたものを使用する場合は、別途追加でバインディングしたクラスを用意する必要がある。

ログにHRTFが有効になって、ビルトインのHRTFの定義ファイルが使われていることが表示される。

08-17 02:18:31.289 D/openal  ( 4327): AL lib: alcOpenDevice: Created device 0x79b38d5de000, "OpenSL"
08-17 02:18:31.292 D/Mono    ( 4327): DllImport searching in: 'openal32.dll' ('./libopenal32.so').
08-17 02:18:31.292 D/Mono    ( 4327): Searching for 'alcCreateContext'.
08-17 02:18:31.296 D/openal  ( 4327): AL lib: UpdateDeviceParams: ALC_HRTF_SOFT = 1
08-17 02:18:31.296 D/openal  ( 4327): AL lib: GetConfigValue: Key frequency not found
08-17 02:18:31.296 D/openal  ( 4327): AL lib: GetConfigValue: Key sources not found
08-17 02:18:31.296 D/openal  ( 4327): AL lib: GetConfigValue: Key sends not found
08-17 02:18:31.296 D/openal  ( 4327): AL lib: GetConfigValue: Key hrtf not found
08-17 02:18:31.297 D/openal  ( 4327): AL lib: GetConfigValue: Key hrtf-paths not found
08-17 02:18:31.297 D/openal  ( 4327): AL lib: GetConfigValue: Key hrtf_tables not found
08-17 02:18:31.297 D/openal  ( 4327): AL lib: DirectorySearch: Searching / for *.mhr
08-17 02:18:31.317 D/openal  ( 4327): AL lib: DirectorySearch: Searching /data/user/0/OpenALApp.OpenALApp/files/.local/share/openal/hrtf for *.mhr
08-17 02:18:31.326 D/openal  ( 4327): AL lib: DirectorySearch: Searching /usr/local/share/openal/hrtf for *.mhr
08-17 02:18:31.326 D/openal  ( 4327): AL lib: DirectorySearch: Searching /usr/share/openal/hrtf for *.mhr
08-17 02:18:31.326 D/openal  ( 4327): AL lib: AddBuiltInEntry: Got new file "Built-In 44100hz"
08-17 02:18:31.326 D/openal  ( 4327): AL lib: AddBuiltInEntry: Adding built-in entry "Built-In 44100hz"
08-17 02:18:31.326 D/openal  ( 4327): AL lib: AddBuiltInEntry: Got new file "Built-In 48000hz"
08-17 02:18:31.326 D/openal  ( 4327): AL lib: AddBuiltInEntry: Adding built-in entry "Built-In 48000hz"
08-17 02:18:31.326 D/openal  ( 4327): AL lib: GetConfigValue: Key default-hrtf not found
08-17 02:18:31.328 D/openal  ( 4327): AL lib: GetLoadedHrtf: Loading Built-In 44100hz...
08-17 02:18:31.328 D/openal  ( 4327): AL lib: GetLoadedHrtf: Detected data set format v2
08-17 02:18:31.329 D/openal  ( 4327): AL lib: GetLoadedHrtf: Loaded HRTF support for format: Stereo 44100hz
08-17 02:18:31.329 D/openal  ( 4327): AL lib: UpdateDeviceParams: Pre-reset: *Stereo, Float, *44100hz, 1024 update size x3
08-17 02:18:31.329 D/        ( 4327): PlayerBase::PlayerBase()
08-17 02:18:31.331 D/        ( 4327): TrackPlayerBase::TrackPlayerBase()
08-17 02:18:31.331 I/libOpenSLES( 4327): Emulating old channel mask behavior (ignoring positional mask 0x3, using default mask 0x3 based on channel count of 2)
08-17 02:18:31.404 W/AudioTrack( 4327): AUDIO_OUTPUT_FLAG_FAST denied by client; transfer 1, track 44100 Hz, output 48000 Hz
08-17 02:18:31.410 D/AudioTrack( 4327): Client defaulted notificationFrames to 708 for frameCount 2124
08-17 02:18:31.415 D/openal  ( 4327): AL lib: UpdateDeviceParams: Post-reset: Stereo, Signed Short, 44100hz, 1024 update size x3
08-17 02:18:31.415 D/openal  ( 4327): AL lib: GetConfigValue: Key stereo-mode not found
08-17 02:18:31.415 D/openal  ( 4327): AL lib: GetConfigValue: Key hrtf-mode not found
08-17 02:18:31.415 D/openal  ( 4327): AL lib: aluInitRenderer: Full HRTF rendering enabled, using "Built-In 44100hz"

実際に聞いていみると、HRTFが有効になっているのは感じるものの、気持ち悪い。有効にしないほうが良いと感じるほどだ。 頭部伝達関数は機器や個々人でいろいろなようなので合っていないと気持ち悪く感じるようだ。

なお、機器や個々人に近い頭部伝達関数の定義ファイルを作成することもできる。それについては次回に見ていきたい。

まとめ

Xamarin.Androidでの

  • OpenAL
    • OpenALAndroid用のビルド
    • HRTFを有効にした際にOpenALにビルトインされているHRTFの定義ファイルで再生

について見た。

メインのOSがWindowsであるものにとって、順調に進められたのは、8月になって、Dockerを使うにあたって、Hyper-Vを稼働している時でも再起動することなくAndroidエミュレータが動くようになったというところが大きく、もともと、それが障壁になっていてなかなか手を動かしたくないという心理が働いていて7月あたりは躊躇していたりした。

ただ、cmakeでのビルドまで、WSLとDockerの連携とかdockerfileを使ってのコンテナの作成とかいろいろ試行錯誤していた。この点については記述していないものの当初想定していたよりも時間がかかった。

OpenTKでのバインディングOpenAL 1.1向けのものでOpenAL Softで拡張されたものには対応していない。HRTFを使いたいと考えるので今後その部分については作成する必要があるだろう。iOSにおいても使いたいと考えた時、iOSではOpenALは存在するが、OpenAL Softではないため、HRTFを使いたいとなるとその対応も必要になるだろう。

HRTFを有効にするとわくわくする。カスタムHRTFについては次回見ていく。自分に近いHRTFファイルを使用すると3D感が増してとても面白い。

次回

takeshich.hatenablog.com

  • HRTFの定義ファイルの作成
  • HRTFの定義ファイルの適用

について触れた。