【Unity】誰でも簡単にイントロ付きループBGMを再生するスクリプト(2015-11-01繋ぎ改善版)

#繋ぎ部分がたまにずれる？

[前回記事：[Unity]誰でも簡単にイントロ付きループBGMを再生するスクリプト](http://qiita.com/tatmos/items/d06ce8af00ad3a0d0ac1)
の[ニコニコ動画](http://www.nicovideo.jp/watch/sm27490070)のコメントでもあったのですが、
確かに切り替わり早い気がしたので

気になったので軽く調べてみた
とりあえずシーンを５回再生して、切り替わりの部分の波形をキャプチャしてみたところ

![image](https://qiita-image-store.s3.amazonaws.com/0/96808/ba070bc9-85cf-eaa4-6b6c-20891d23a2ec.png)

３回目は確実に早いですね。
(たまたま、動画撮影時に発生していたのか・・・）

#Start関数のPlayが遅れている可能性

前の音の長さをとって再生しているけど、PlayScheduledがずれるのは考えにくいので、
おそらく
シーンStartでの初回再生処理が遅れることがあるのかも。

introの方もPlayScheduledで再生するように改変。

https://github.com/tatmos/BGM_Intro_loop/blob/master/intro_loop/Assets/BGMController.cs

<pre class="prettyprint">
using UnityEngine;
using System.Collections;

public class BGMController : MonoBehaviour {

	public AudioClip intro;
	public AudioClip loop;
	public float delay = 0.1f;

	AudioSource[] audioSource = new AudioSource[2];

	// Use this for initialization
	void Start () {
		audioSource[0] = this.gameObject.AddComponent<AudioSource>();
		audioSource[1] = this.gameObject.AddComponent<AudioSource>();
	
		if(intro != null){
			audioSource[0].playOnAwake = false;
			audioSource[0].clip = intro;
			audioSource[0].PlayScheduled(AudioSettings.dspTime + delay);
			if(loop != null){
				audioSource[1].playOnAwake = false;
				audioSource[1].clip = loop;
				audioSource[1].loop = true;
				audioSource[1].PlayScheduled(AudioSettings.dspTime + delay + intro.length);
			}
		}
	}
}
</pre>

#どれくらい遅延させるか？

繋ぎがずれるのは気になる。
BGMなので全体が多少遅れても目立たない。
でも、あんまり遅らせすぎても良くない。

前のめり時間が、64分音符くらいだったので、
BPM120(1beat = 500msec) の64分音符の長さは　
500/8 = 62.5msec

ってことは0.1秒くらい遅延させてあげれば大丈夫かな？

![image](https://qiita-image-store.s3.amazonaws.com/0/96808/4fac2c26-693a-9197-1f6d-0857c5e71b66.png)

５回くらい鳴らしてもずれは発生しない様子になりました。
上のインスペクタでDelayを変更できますが、試しにDelayを0にしてしまうとずれが3回目くらいの再生で発生しました。

#教訓

- Play関数とPlayScheduled関数をまぜて使わないようにする（同期させたければ） 

再生関数がいろいろあったら、違う経路の再生ではなく、同じ種類の関数で再生するのが良いですね。
（ってことは、まれに62.5msecくらいPlayは遅れたり遅れなかったりということなのだろうか）

 音楽にいろいろ同期させたいとかいう場合はgeekdrums氏の[MusicEngine](https://github.com/geekdrums/MusicEngine)おすすめしておきます。

#余談
「誰でも簡単」の落とし所は？
ということでオーディオの細かい話になるのですが、
ただ鳴らすだけでも用途によっては選択が迫られる感じで、どうやら「誰でも簡単」にはならない。
今回は、誰→「ゲームジャム参加する気合の入った輩」で素材が綺麗にトリミング（イントロ、ループ）分割されている（本当にありがたい。[バンダイナムコカタログIPゲームジャム](https://open.channel.or.jp/gamejam.php)運営の方ありがとうございます）
且つUnity上でという限定なら大丈夫かな。
以下、おすすめするADX2の場合の例で．．．

## 再生開始タイミングについて
ADX2の場合オーディオサーバー周期という間隔で処理が行われるので、その間隔が時間的な分解能で再生開始がずれる可能性はあります。
このサーバー周期は曲者で間隔が短すぎても長すぎても一長一短あります。
短すぎると反応が良いけど処理負荷が上がる傾向、長いと反応悪いしメモリも食うけど大量再生しても音途切れとか起きないようになる。
ゲームタイトルに合わせてある程度調整できるけど、設定可能な実用範囲は厳しい。

演出的にならす音数を減らすか、増やすかなどにもよるし、楽器音のように超高速レスポンスが欲しい音もあれば、ボイスやBGMのように多少立ち上がりが遅くても気にならない音も混ざって、バランスとしてどちらが多いかとか悩みが多い。
超低遅延を求めるならエンジンミドルウェアやドライバ層飛び越えて低レベル（直接ハードリソース叩くとか）も。それすらも策が無い場合もある。処理制御の調整やメモリ容量制限など厳しい世界が待っているし安定して使え無いかもしれない→「誰でも簡単」とはいかないものもある。

## シーケンスの解像度
再生開始とは別なものでシーケンスとかブロックの繋ぎはより精度高くタイミング処理しているのでほとんど気にしなくても良いかも。（ストリーム再生でも先読みさせておくなどテクニックがある）
ほぼずれないけれど、シーケンスのループとかブロック切り替えタイミングとかに誤差が発生するタイプ。

シーケンスに似てるけど少し違う「セレクタによるトラック遷移」は例外的に、波形サンプルレベルでの同期切り替えや途中切り替え方式で、用途が限定されたデータの持ち方になる。テンポ違いでも曲を切り替え可能。

## サンプル精度
波形をサンプル精度で連結再生したい場合もある。主に波形連結などの用途になります。
簡単には、イントロ付きループ波形を使う。
プログラムも込みならシームレス連結再生を行うといった方法があります。

## ストリームの場合
ストリームはファイルアクセスを行うのでかなり開始が遅れる。オーディオサーバーの次の処理とかにまわされてしまう場合も。メモリ再生なら同じオーディオサーバー処理内で動ける。ただし、メモリを食う。
なので、最近追加された「ゼロレイテンシーストリーム」を使うのもあり。