Showing posts with label OpenSL. Show all posts
Showing posts with label OpenSL. Show all posts

Friday, December 25, 2015

OpenSL ESでActivityが非アクティブになった時に音を停止する簡単な方法

前回OpenSL ESでサウンドを再生しましたが、Activityが非アクティブになっても音が鳴り続ける問題がありました。それに対しての簡単な対処法として、バッファを細かく分けてEnqueueしてみました。Enqueueは32768バイト単位でしています。(サイズは適当です)

最終アップデートからの経過時間が0.5秒を超えたらコールバック無視します。この方法の長所は、コールバック一つでループ再生と非アクティブになった時の音停止を同時に実装でき、コードが簡潔になります。Activityから非アクティブの通知を受け取る必要もありません。短所としては、アップデートループで長い処理をすると音が切れてしまうことです。

また、停止した音を再開する処理を盛り込んでいません。ここは、ゲーム毎の実装依存が多いと思いあえてしませんでした。例えば多くの場合は再開すべき音はBGMだけであったりするでしょう。

AndroidのOpenSL ESはコールバックは別スレッドから来るので特に気をつける必要があります。Android特有の実装についてはNDKのdocs/Additional_library_docs/opensles/index.html に説明があるので、一度目を通しておくと良さそうです。

Saturday, December 12, 2015

実践OpenSL ES、AndroidでWAVファイル再生

OpenSL ESによるサウンドエンジンを使い物になるように書いてみます。

まず、気づくのはDirectXを彷彿させるインターフェース型のAPIであることです。多くのメソッドはSLresultで結果を返し、Destroyでリソースを解放して終了します。素直に書くと冗長なコードになりそうですが、まず最初にDirectX開発者にはおなじみの手法を導入します。

SafeDestroyはDestroy呼び出しと同時にnullptrを代入してダングリングポインタにならないようにします。

SLHandleErrorというのは、各種メソッドがエラーコードを返したらソースコード上の行数と関数呼び出しの概要をログに出力するためのマクロです。Direct3D9のサンプルでは"V"という一文字のマクロが多用されていましたが、それです。

SLCallは、インターフェース呼び出しをすっきり書きたいがために書いたマクロです。これを使ってエンジンの初期化と終了を書いてみます。

エンジンの初期化と終了が書けました。

DirectXから来ると戸惑うのが、SLObjectItfとその他インターフェースに分かれていることです。OpenSLでは全てのインスタンスの実体はSLObjectItfであって、何らかのメソッド呼び出しが必要になったらSLObjectItfから追加のインターフェースを取得します。また、SLObjectItfをDestroyするとSLObjectItfから取得したインターフェースも一緒に無効になります。

engineEngineがSafeDestroyではなくnullptrを代入しているのは、engineObjectをSafeDestroyした時に一緒に消滅しているからです。

次は、WAVファイルをロードして"audio player"を作ってみます。

CreateAudioPlayerで、WAVファイルの特性(ステレオ/モノラル、サンプリングレート、ビット数)に合わせた"audio player"を生成します。WaveFormatExはWindows APIにあるWAVEFORMATEX構造体で、この情報からSLDataFormat_PCM構造体を構築します。リファレンス通りに書くとまたコードが長くなりそうですが、例えばSL_SAMPLINGRATE_44_1は44100000と定義されていたりします。samplesPerSecondを1000倍して代入すれば良いわけです。事前定義値は https://www.khronos.org/registry/sles/api/1.1/OpenSLES.h にあります。

次はWAVファイルの再生部です。

Enqueueで再生するバッファを指定します。ループ再生する場合はコールバックを登録して、コールバックからEnqueueを繰り返します。コールバック設定前にSL_PLAYSTATE_STOPPEDを明示するのはステートがSL_PLAYSTATE_STOPPEDではない場合にSL_RESULT_PRECONDITIONS_VIOLATEDエラーが発生するためです。これはPlayメンバ関数が二回目に呼ばれた場合に起こります。

これで、OpenSLの初期化からWAVファイルの再生までができました。ところで、この方法だとActivityが非アクティブになっても音が鳴り続けるという問題があります。この解決は次回書きます。