Androidアプリで前奏より後だけループするBGMを流す
趣味でAndroid向けアクションゲームを作っている。
年末年始を使ってBGMとSEを組み込んでいた。
AndroidでのBGM再生にはMediaPlayerクラスを使う。
res/raw/以下にoggファイルを突っ込む
(mp3でもいいけど端末によってはあまり嬉しくないことがあるとか?)
とかその辺はいいんだけど、ゲームでよくある
「前奏が一回だけ流れた後、ループ部だけが延々繰り返される」
BGMを流したかった。
流すBGMの音楽ファイルは、あらかじめ前奏部とループ部に分割しておく。
で、両方の曲データを使ってMediaPlayerのインスタンスを作る。
MediaPlayerのインスタンスは4つまでしか作れないとかあるいは10個までとか
いろんな情報があるけど、何にしろアプリ内で使う曲全部を
起動時に全部読み込んでおくのはできないらしい。
曲を変更するときはインスタンスも作り直し。
overture=MediaPlayer.create(context, nowPlaying.overture); overture.setAudioStreamType(AudioManager.STREAM_MUSIC); overture.setLooping(false); refrain=MediaPlayer.create(context, nowPlaying.refrain); refrain.setAudioStreamType(AudioManager.STREAM_MUSIC); refrain.setLooping(true);
overtureとrefrainは両方MediaPlayerクラス。
nowPlayingにはBGMの情報がいろいろ収まっている。(名前もそのまま、BGMInfoってクラスを作った)
MediaPlayer.createで渡しているのは音楽データのリソースID。
もちろんR.raw.bgm1とかを直接渡してもいい。
んで、ここで2つできたMediaPlayerインスタンスのうち、
overtureを一回だけ流してから途切れずにrefrainに移行したい。
AndroidのAPI Levelが16(Jelly Bean)以上ならとても簡単だったりする。
overture.setNextMediaPlayer(refrain);
これだけ。overtureの再生が終わったらすぐにrefrainの再生を開始してくれる。
曰くas seamlessly as possible.ギャップレス再生も大抵可能。
だけどもうちょい古い端末でも動作させたい。
MediaPlayerにはOnCompletionListenerなんてのがある。
曲の再生が終わったら呼ばれるので、ここでrefrainの再生を開始させようと思ったけど、
どうも曲の切り替わりタイミングで一瞬音が途切れるのでボツ。
結局力技でやることにした。
とりあえず再生開始時にはAPI Levelを調べて、setNextMediaPlayerが使えるならセット。
古い端末ならovertureの総再生時間を調べる。
if(Build.VERSION.SDK_INT>=16){ overture.setNextMediaPlayer(refrain); }else{ refrain.start(); refrain.pause(); nowPlaying.overtureLength=overture.getDuration(); } overture.start();
refrainの再生開始時に少しでも時間をかけないように、
一旦startしてからすぐにpauseしてる。…効果があるのかは微妙なところ。
そして、アプリ内で定期的にovertureが終わりそうか否かチェック。
public static void conduct(int interval){ if(Build.VERSION.SDK_INT<16 && overture!=null && refrain!=null){ if(overture.isPlaying() && overture.getCurrentPosition() > nowPlaying.overtureLength-interval/2) { Thread t=new Thread(new Runnable(){ public void run(){ overture.stop(); refrain.start(); }}); t.start(); } } }
intervalはこのconductメソッドを呼び出す周期。ゲームのフレームレートとか。
overtureの現在時間が、終了時間間際とか直後とか、
その辺だったらrefrainに移行する。この辺のタイミング判定は要調整かも。
アクションゲームでの使用を想定しているので、
曲の切り替えで処理落ちとかさせたくない。
という訳で非同期で曲変更。…これも果たして意味があるのかどうか。
やってみた感じだと違和感なく繋がっている模様。