Unityで既定以外の録音デバイスを使う(口パク)

#背景
Unityの口パクと言えばOVRLipSyncです。が、そもそも他のOVRと名前が被っていたり、画面外を操作すると録音が止まるなど、用途によっては不要な機能もあります。そしてなにより、<font color="Red">Unityバージョンによっては、録音デバイスを変更しようと、既定の録音デバイスを切り替えようと、意図した録音デバイスから音声入力を得られない事、ありませんか？</font>
https://issuetracker.unity3d.com/issues/can-not-receive-information-about-microphone-devices-on-specific-computer-builds

#対策
NAudioを使います。
https://github.com/naudio/NAudio
※CodePlexの方は古くなったので気を付けましょう。

#注意
諸刃の剣である可能性が高いです。
http://furipro.blog.fc2.com/blog-entry-8.html
ページ下部の「2017/05/27 追記」参照

私の環境では今のところ「たまにクラッシュ」する程度で済んでいます。
クラッシュする原因は別かもしれませんが……

#NAudioをUnityで使うには？
NAudio.dllをプロジェクトに取り込むだけです。
どこに置いても大丈夫です。
https://docs.unity3d.com/jp/540/Manual/UsingDLL.html

#スクリプト
```csharp
using UnityEngine;
using NAudio.Wave;

public class MoveMouth : MonoBehaviour
{
    // Inspectorで録音デバイスを指定する場合の名前(未指定は既定)
    [SerializeField]
    string DeviceName;

    // 口パク対象(SkinnedMeshRenderer)のIndex
    [SerializeField]
    int Index;

    // 入力音量に口パク具合を合わせる係数
    [SerializeField]
    float Gain = 1;

    // このスクリプトは口パク対象(SkinnedMeshRenderer)に付ける前提
    SkinnedMeshRenderer Skinned;

    // 入力音声データ
    WaveIn Wave;

    // 入力音声データを音量としてBlendShapeに設定する値
    float Weight = 0;

    void Start()
    {
        int selectDevice = 0;

        if (DeviceName == "")
        {
            // 録音デバイス未指定なら既定
            DeviceName = WaveIn.GetCapabilities(0).ProductName;
        }
        else
        {
            // 録音デバイスが指定されていたら探す
            for (int i = 0; i < WaveIn.DeviceCount; i++)
            {
                Debug.Log(WaveIn.GetCapabilities(i).ProductName  + " : " + DeviceName);
                if (WaveIn.GetCapabilities(i).ProductName == DeviceName)
                {
                    selectDevice = i;
                    break;
                }
            }
        }

        Debug.Log(selectDevice);

        // NAudioの録音初期化
        Wave = new WaveIn();
        Wave.DeviceNumber = selectDevice;
        Wave.DataAvailable += Wave_DataAvailable;
        Wave.StartRecording();

        Skinned = GetComponent<SkinnedMeshRenderer>();
    }

    // 音量取得
    void Wave_DataAvailable(object sender, WaveInEventArgs e)
    {
        Weight = 0;
        var buffer = new WaveBuffer(e.Buffer);
        for (int index = 0; index < e.BytesRecorded / 2; index++)
        {
            Weight += Mathf.Abs(buffer.ShortBuffer[index]);
        }
        Weight /= e.BytesRecorded / 2;
    }

    // 取得した音量に係数をかけて調整してBlendShapeに設定
    void Update()
    {
        Skinned.SetBlendShapeWeight(Index, Weight * Gain);
    }

    void OnDestroy()
    {
        Wave.StopRecording();
    }
}
```
<font color="Red">※Wave_DataAvailableのe.BytesRecorded / 2とかShortBufferは録音デバイスに合わせる必要あるかもです。</font>

#設定
Unityちゃんであれば、MTH_DEFにこのスクリプトを付けて以下のように設定します。

Device Name : 録音デバイス名※
Index : MTH_DEFのBlendShapesから対応させたい口の形の順番(上から順に0から数える)(「あ」なら6)
Gain : 初期値で一度起動して、利用環境で丁度良い値を探す(私は0.05)

※録音デバイス名は空のまま、使いたい録音デバイスを既定にして起動すると名前が自動で入るので、それをコピーして一旦終了した後に設定すれば、次回以降は既定以外のデバイスを使えます。

#備考
いわゆる生データが取れるので、ちゃんとやればOVRLipSyncのように動かせるかもしれませんし、もしかしたらOVRLipSyncの入力として使えるような気もしますが、試してもいませんし、確認もしていません。上手く行ったら教えてもらえたら嬉しいです。

#参考
https://github.com/naudio/NAudio/blob/master/Docs/RecordingLevelMeter.md
http://www.baku-dreameater.net/archives/10111