ADX2LEとWebプレーヤー（#SoundGameJam LudumDare35 Voipadius)

Ludumdare 35に[第5回サウンドゲームジャム](http://effectorhack.connpass.com/event/27404/)で作った[Voipadius](http://ludumdare.com/compo/ludum-dare-35/?action=preview&uid=41153)で参加したのですが、
やっぱりWebプレーヤーで遊べると気軽に遊んでもらえそうなので対応してみた。

[Webプレーヤー版Voipadius（ボイパディウス)](http://chahix.ddo.jp/~chahi/unity/ludumdare_35/Voipadius/Voipadius.html)
ボイスパーカッションバトルを味わえるような２人以上で遊ぶ楽器（？）です。
出てくるキャラに合わせてボタンを押す。人数が多い場合は適当に交代する。
音が鳴る。すべての音は声の録音素材です。（ドラムとかトランペットとか動物とかすべてボイパ。すごい。）

![image](https://qiita-image-store.s3.amazonaws.com/0/96808/3432a6bd-6865-ae47-ae38-d8dc0373ac5c.png)

#ダウンロード
[AtomCraftプロジェクトファイル(Voipadius_AtomProject.zip)](http://chahix.ddo.jp/~chahi/unity/ludumdare_35/Voipadius_AtomProject.zip)
[Unityプロジェクト(Voipadius.unitypackage)]
(http://chahix.ddo.jp/~chahi/unity/ludumdare_35/Voipadius.unitypackage)

波形素材とAtomCraftプロジェクトの変更や再利用は自由にどうぞ。
音を差し替えてみたりして遊んでみてね。
(Unity5.3.4f1,ADX2LEv202)

scenesのgameSceneとVirtualNanoPadSceneを使ってます。
追加しておいてください。
MidiJackのアクセスで止まってしまう場合はコメントアウトしてみて。

#やることは

- ADX2LEのキューの名前、IDを修正
- ADX2LEの音をwav化（バウンス）する
- ADX2LEの警告を無効化する
- AtomSourceのキュー再生をAudioSourceの再生にする

の4ステップ。

#ADX2LEのキューの名前、IDを修正

Unityの標準再生は波形ファイルをAudioClipにするので、対応するAudioClipを作るための波形を用意する。
![image](https://qiita-image-store.s3.amazonaws.com/0/96808/0339d307-c98d-e13d-d9e1-7fa4d49b78c2.png)

##キュー名とキューIDで再生していたのだが、

IDはやめて名前を番号にすることで、すべて名前で呼べるようにした。

MIDIデバイスからの入力Note番号をそのままキューIDにするような形で作っている。
（60から始まるように少しずらしているけど）
0は無音用として、
キューIDがMIDINoteNoとして動作するようにしている。

##元々あった名前はコメントに退避
![image](https://qiita-image-store.s3.amazonaws.com/0/96808/3a97a783-dffb-2edf-7851-ed28ae7183e4.png)
流し込みペーストを使って名前とIDを入れ替え。

#ADX2LEの音をwav化（バウンス）する
![image](https://qiita-image-store.s3.amazonaws.com/0/96808/addf0689-8f6b-942c-ef01-d08ba5a16841.png)

###バウンス設定を修正して、一つずつバウンス。

![image](https://qiita-image-store.s3.amazonaws.com/0/96808/141846ae-c666-f4dc-3786-2873e3d264a5.png)

###マテリアルの「bounce」フォルダにキュー名で波形が作成される

あらかじめ名前を修正していたのでいい感じに。

![image](https://qiita-image-store.s3.amazonaws.com/0/96808/3dade859-046e-bb0d-a2ba-0e2d3856d438.png)

###マテリアルのフォルダに波形が出来る

たまに再生位置がずれたりして失敗している場合があるので再生してチェックし、失敗した波形はバウンスをやり直す。
あと、ランダムなども気に入らない場合はバウンスをやり直す。

![image](https://qiita-image-store.s3.amazonaws.com/0/96808/c333f685-5baf-c281-c4fc-45e14e7939b1.png)

あと、波形エディタで若干トリミングしています。

#ADX2LEの警告を無効化する

Webプレーヤーでビルドするといくつか警告でビルドを中断されるので
以下の修正

``` CriAtom(24-30).cs
	#endif
#elif UNITY_WIIU || UNITY_PSP2 || UNITY_PS3 || UNITY_PS4 || UNITY_WEBGL
	#define CRIWARE_UNSUPPORT_SEQUENCE_CALLBACK
#elif UNITY_WEBPLAYER
#else
	#error undefined platform if supporting sequence callback
#endif

```
``` CriAtomExVoicePool(48-54).cs
#elif UNITY_PSP2
		StandardMemory			= StandardMemoryNsrVoicePoolId,		/**< 機種標準のメモリ再生ボイスプールID */
		StandardStreaming		= StandardStreamingNsrVoicePoolId,	/**< 機種標準のストリーミング再生ボイスプールID */
            #elif UNITY_WEBPLAYER
#else
		#error unsupported platform
#endif

```

``` CriAtomExVoicePool(70-76).cs
#elif UNITY_PS4
		Atrac9Memory			= 6,								/**< [PS4] ATRAC9メモリ再生ボイスプールID */	
		Atrac9Streaming			= 7,								/**< [PS4] ATRAC9ストリーミング再生ボイスプールID */
            #elif UNITY_WEBPLAYER
#else
		#error unsupported platform
#endif
```
UNITY_WEBPLAYERの#ifをつけただけ。

#AtomSourceのキュー再生をAudioSourceの再生にする

ファイル名でサクッとやりたかったのですが、
ここから少し面倒なことに、Unity5.3.4f1のWebプレーヤーだとResources.Loadがうまくいかない。
（あ、Resourcesに入れてなかったからか．．．とこれ書いてる時に気がついたがまぁいいや）

ので、[Resources.Loadを使わない方法でのシーンでのclip管理]的なTipsということで・・・


名前リスト用意して、clipを名前で指定できるようにしている。

![image](https://qiita-image-store.s3.amazonaws.com/0/96808/5130d23a-6701-f80c-38e7-71fbecc06f52.png)

##シーンをキューシートみたいに見せかける話

シーンに無理やりすべてのAudioClipをシーンに追加。

bounceフォルダをUnityプロジェクトに入れて、
空のGameObject作って、その下に配置。

この時、名前の後ろに「 (1)」が勝手につく。
このAudioClipをclipArrayに入れて呼び出せるようにした。（ちと回りくどいかもね・・・）
でも、シーン単位で複数AudioClipを管理しているみたいにできるね。きっと。

![image](https://qiita-image-store.s3.amazonaws.com/0/96808/e6b3cf39-3386-876c-41b6-2087bd49b3ca.png)

``` PlayADX2.cs
public class PlayADX2 : SingletonMonoBehaviourFast<PlayADX2>
{
    public Text beatCountText;

    #if UNITY_WEBPLAYER
    AudioSource[] source = new AudioSource[2];
    int souceCount = 0;
    Dictionary<string,AudioClip> clipArray = new Dictionary<string,AudioClip>();
    string[] clipNameList = {
        "60",
        "61",
        "62",
        "63",
        "64",
        "65",
        "66",
        "67",
        "68",
        "69",
        "70",
        "71",
        "72",
        "73",
        "74",
        "75",
        "76",
        "BD",
        "blulu",
        "HH",
        "hituzi_0",
        "hituzi_1",
        "hituzi_2",
        "hituzi",
        "neco_0",
        "neco_1",
        "neco_2",
        "neco",
        "rest",
        "SD",
        "SD2",
        "TPLow",
        "TPLow2",
        "TPLow3"
    };

    AudioClip NameToClip(string name)
    {
        return clipArray[name];
    }
    AudioClip IdToClip(int id)
    {
        if(id < 60){

            AudioClip clip = null;
            switch(id)
            {
                case 0:clip = clipArray["rest"];break;
                case 1:clip = clipArray["BD"];break;
                case 2:clip = clipArray["blulu"];break;
                case 3:clip = clipArray["SD"];break;
                case 4:clip = clipArray["TPLow"];break;
                case 5:clip = clipArray["SD2"];break;
                case 6:clip = clipArray["TPLow2"];break;
                case 7:clip = clipArray["HH"];break;
                case 8:clip = clipArray["TPLow3"];break;
                case 9:clip = clipArray["hituzi"];break;
                case 10:clip = clipArray["neco"];break;
                case 11:clip = clipArray["hituzi_0"];break;
                case 12:clip = clipArray["neco_0"];break;
                case 13:clip = clipArray["hituzi_1"];break;
                case 14:clip = clipArray["neco_1"];break;
                case 15:clip = clipArray["hituzi_2"];break;
                case 16:clip = clipArray["neco_2"];break;

            }
            return clip;
        }

        return clipArray[id.ToString()];
    }
    
#else
    CriAtomSource source;
    #endif

    public GameManager2 gm;

    void Start()
    {
        #if UNITY_WEBPLAYER
        for(int i = 0;i<2;i++){
            source[i] = this.gameObject.AddComponent<AudioSource>();
            source[i].volume = 0.71f;
        }

        foreach(string name in clipNameList)
        {
            //AudioClip newClip = Resources.Load("bounce/"+name,typeof(AudioClip)) as AudioClip;
            GameObject obj = GameObject.Find(name + " (1)");

            if(obj == null)
            {
                Debug.LogWarning("obj null " + "name");
            }

            AudioSource newClip = obj.gameObject.GetComponent<AudioSource>();

            if(newClip == null)
            {
                Debug.LogWarning("null " + "bounce/"+name);
            } else {

                //Debug.Log(name);
                clipArray.Add(name,newClip.clip);
            }
        }

        #else
        source = this.gameObject.AddComponent<CriAtomSource>();
        #endif

``` 

###再生部分

AudioSourceは２つくらいにした。（同時に２つしか鳴らないけどまぁいいか）
IDでも名前に変換して呼び出すようにした。

``` PlayADX2.cs
    /// <summary>
    /// キュー名で再生
    /// </summary>
    /// <param name="name">Name.</param>
    public void Play(string name)
    {
        if (isSequencePlaying)
        {
            quantizePlayCueName = name;
        } else
        {
            #if UNITY_WEBPLAYER
            source[souceCount%2].clip = NameToClip(name);
            source[souceCount%2].Play();
            souceCount++;
            #else
            source.Play(name);
            #endif
        
        }
    }

    public void PlayId(int id)
    {

        //Debug.Log("PlayId" + id.ToString());
        if (isSequencePlaying)
        {
            quantizePlayCueId = id;
        } else
        {
            #if UNITY_WEBPLAYER
            source[souceCount%2].clip = IdToClip(id);
            source[souceCount%2].Play();
            souceCount++;
            #else
            source.Play(id);
            #endif
        }
    }


```

### AISACとかも呼べないので、

#if UNITY_WEBPLAYERで呼ばないようにした

``` PlayADX2.cs
                    if (ccNumber == 1)
                    {
                        #if UNITY_WEBPLAYER
                        #else
                        source.SetAisac(2, ccValue);    //  filter
                        #endif
                    }
                    if (ccNumber == 2)
                    {
                        #if UNITY_WEBPLAYER
                        #else
                        source.SetAisac(3, ccValue);    //  fx1
                        #endif
```

＃おまけ

##おまけ1.実は簡易MMLとして、32分音符固定MMLになってます。
オクターブもないですけど・・・

![image](https://qiita-image-store.s3.amazonaws.com/0/96808/a3ade840-4da4-43d0-fb91-8483413be164.png)

##おまけ2.[MusicEngine](https://github.com/geekdrums/MusicEngine)ぽいもののの簡易的なものとしてクオンタイズプレイをつけてあります。
シーケンス再生中は16分音符でクオンタイズ再生されます。

``` ADX2Play.cs
               if ((beatCount32 % 2) == 0)
                {
                    if (quantizePlayCueName != "")
                    {
                        #if UNITY_WEBPLAYER
                        source[souceCount%2].clip = NameToClip(quantizePlayCueName);
                        source[souceCount%2].Play();
                        souceCount++;
                        #else
                        source.Play(quantizePlayCueName);
                        #endif
                        quantizePlayCueName = "";
                    }
                    if (quantizePlayCueId != 999)
                    {
                        #if UNITY_WEBPLAYER
                        source[souceCount%2].clip = IdToClip(quantizePlayCueId);
                        source[souceCount%2].Play();
                        souceCount++;
                        #else
                        source.Play(quantizePlayCueId);
                        #endif
                        quantizePlayCueId = 999;
                    }
                }
```

##おまけ3.[MidiJack](https://github.com/keijiro/MidiJack)をデバイスごとに受け取れるように変更

ただ、iOSとかWEBPLAYERだと読んだ時に止まってしまうようなので回避もしてる。

``` ADX2Play.cs
   void Update()
    {
        uint playerDeviceId = 0;
        #if UNITY_WEBPLAYER || UNITY_IOS
        #else
        switch (gm.turn)
        {
            
            case 1:
                playerDeviceId = MidiDriver.Instance.connectedSourceArray [MidiDriver.Instance.connectedSourceArray.Count - 1];
                break;
            case 2:
                playerDeviceId = MidiDriver.Instance.connectedSourceArray [MidiDriver.Instance.connectedSourceArray.Count - 2];
                break;
            
        }
        #endif

        #region padInput
        for (int noteNumber = 0; noteNumber < 128; noteNumber++)
        {
            if (playerDeviceId > 0)
            {

                if (MidiMaster.GetKeyDown(playerDeviceId, noteNumber))
                {
                    //Debug.Log("noteNo" + noteNumber.ToString());

                    if (noteNumber >= 36 && noteNumber < 36 + 16)
                    {
                        // Scene1
                        this.PlayId(noteNumber - 35);
                    }
                }
            }
        }
        #endregion
```

#終わりに

ADX2LEをプロトタイプとして使っていて、
今回みたいに未対応プラットフォーム(Webプレーヤーとか）だった場合に、
ADX2LEを複数波形の管理ツールとして使うのもありかもと思った。
（ボリューム調整やコメントつけたり、名前変更とかも楽だし。）
Unityのマルチシーンロード機能を使えば、
「シーン＝キューシート」と見たてて、複数オーディオのパッケージとして、シーンを作成していくとかできそう。