サウンド処理は非同期で その12013/11/28

例えばサウンドの再生、OGGファイルを読み込んで再生するような場合、
・ファイルの読み込み
・オブジェクトの初期化などOpenSLで必要な各種設定
・出だしのデータのデコード
・再生
と、結構多くの処理が必要です。
その処理の間メインを止めてしまっているのは何とも忍びないので、サウンドは別のスレッドで非同期に処理するようにしてみます。

具体的には、
・再生などの命令が来たら、その命令とパラメータをキューに貯める
・別のスレッドで非同期にキューを監視して、命令を処理する
という流れになります。

キューを管理するスレッドの処理は javaで行います。

SoundManager.java
package sys;

import java.util.concurrent.ArrayBlockingQueue;
import android.os.AsyncTask;
import android.util.Log;


/******************
    サウンド管理
 ******************/
public class SoundManager extends AsyncTask<Void, Void, Void>
{
	static private SoundManager		manager;							// サウンド管理タスク
	static private ArrayBlockingQueue<SoundCommand>		queue;			// コマンドキュー


	/************
	    初期化
	 ************/
	static
	public void		init()
	{
		queue	= new ArrayBlockingQueue<SoundCommand>(32, true);		// キュー作成
		manager	= new SoundManager();
		manager.execute();
	}

	/**********
	    終了
	 **********/
	static
	public void		quit()
	{
		queue	= null;
		manager	= null;
	}

	/******************
	    コマンド予約
	 ******************/
	static
	public void		set_command(short _channel, short _command, int _data, int _size, short _loop, float _volume)
	{
		try {
			queue.put(new SoundCommand(_channel, _command, _data, _size, _loop, _volume));
		}
		catch (InterruptedException e) {}
	}

	/******************
		コマンド停止
	 ******************/
	static
	public void		stop_command()
	{
		manager.cancel(true);
		while ( queue != null ) {
			try {
				Thread.sleep(1);
			}
			catch (Exception ex) {
				ex.printStackTrace();
			}
		}
	}

	@Override
	protected void	onPreExecute() {}

	public native void	sendSoundCommand(short _channel, short _command, int _data, int _size, short _loop, float _volume);

	/**********
	    稼働
	 **********/
	@Override
	protected Void	doInBackground(Void... params)
	{
		SoundCommand	_command;

		while ( !isCancelled() ) {
			try {
				_command = queue.take();					// コマンド取得
				if ( _command != null ) {					// コマンド実行
					sendSoundCommand(_command.channel, _command.command, _command.data, _command.size, _command.loop, _command.volume);
					_command = null;
				}
			}
			catch (InterruptedException e) {
				queue.clear();
			}
		}
		queue.clear();
		queue = null;
		return	null;
	}

	protected void	onPostExecute(Void result) {}
}


/**********
    命令
 **********/
class SoundCommand
{
	public short	channel;		// チャンネル
	public short	command;		// コマンド
	public int		data;			// データ
	public int		size;			// サイズ
	public short	loop;			// ループ回数
	public float	volume;			// 音量

	/********************
	    コンストラクタ
	 ********************/
	public SoundCommand(short _channel, short _command, int _data, int _size, short _loop, float _volume)
	{
		channel	= _channel;
		command	= _command;
		data	= _data;
		size	= _size;
		loop	= _loop;
		volume	= _volume;
	}
}

/******************** End of File *******************************************************/

重要なのは2つ、
・AsyncTaskで非同期処理
・ArrayBlockingQueueでキューの管理
ということくらいです。
サウンド管理と名前はついていますが、サウンドの処理自体は native側で行っていますので、ここでは"サウンド用のデータ"を管理しているだけです。

native側からは set_commandコマンドが呼ばれます。
ここでキューに貯められた命令を doInBackgroundの中で取り出して、nativeの関数 sendSoundCommandを呼び出して実際のサウンド処理を行うようになっています。


native側の処理は、また次回以降に。