【Unity×C#】マイクからオーディオを取得しファイルに保存する

# 概要
Unity×C#の環境でマイクからオーディオを取得しWAVファイルとして保存するデモです。

# 開発環境
Windows 10
Unity 2019.4.31f1
Api Compatibility Level .NET Standard 2.0

# 実装
詳細はスクリプト中にコメントで記載しています。
## DemoAudioRecorder.cs
```csharp
using System.Collections;
using System.Linq;
using UnityEngine;

public class DemoAudioRecorder : MonoBehaviour
{
    // Start is called before the first frame update
    private string microphone;
    private AudioClip microphoneInput;
    private const int RECORD_LENGTH_SEC = 10;
    private const int SAMPLE_RATE = 41100;

    void Start()
    {
        // 利用可能なマイクを検出
        microphone = Microphone.devices.FirstOrDefault();
        Debug.Log("microphone: " + microphone);
        if (microphone == null)
        {
            Debug.LogError("No microphone found");
            return;
        }

        // 第二引数をtrueにすると循環バッファとしてループ保存
        microphoneInput = Microphone.Start(microphone, false, RECORD_LENGTH_SEC, SAMPLE_RATE);
        Debug.Log("録音を開始します。何か話してください。");

        // Coroutine（非同期処理）でWaitAndExecuteメソッドを実行
        StartCoroutine(WaitAndExecute());
    }

    // Update is called once per frame
    void Update() { }

    /// <summary>
    /// 10秒間待機して、録音結果をWAVファイルに保存
    /// Unity自体の処理を止めないようにコルーチンを利用して非同期処理にしています。
    /// </summary>
    IEnumerator WaitAndExecute()
    {
        // 10秒間待機
        yield return new WaitForSeconds(10);
        Debug.Log("録音を終了し、WAVファイルに保存します。");

        // 保存先ファイルの設定
        var filePath = string.Format("{0}/{1}/{2}", Application.persistentDataPath, "recordings", "recordedAudio.wav");
        Debug.Log("filePath: " + filePath);

        // AudioClipからWAVファイルを作成
        SaveWavFile(filePath, microphoneInput);
    }

    /// <summary>
    /// AudioClipからWAVファイルを作成
    /// WavUtilityには以下のコードを利用。ただし、FromAudioClipメソッドの一部を修正
    /// https://github.com/deadlyfingers/UnityWav/tree/master
    /// </summary>
    private void SaveWavFile(string filepath, AudioClip clip)
    {
        // AudioClipからWAVファイルを作成
        byte[] wavBytes = WavUtility.FromAudioClip(clip, filepath, true);
    }
}

```

## WavUtility.cs（微修正したFromAudioClipメソッド部分のみ）
WAVファイルとして保存するスクリプトにはWavUtilityを一部書き換えて利用しました。

https://github.com/deadlyfingers/UnityWav/tree/master

注意：以下のスクリプトは一部分のみです。これだけでは機能しません。
```csharp
    // すべてコメントアウト
    // public static byte[] FromAudioClip(AudioClip audioClip)
    // {
    // 	string file;
    // 	return FromAudioClip(audioClip, out file, false);
    // }

    // filepathがoutである必要はないので、outを削除
    public static byte[] FromAudioClip(AudioClip audioClip, string filepath, bool saveAsFile = true, string dirname = "recordings")
    {
        MemoryStream stream = new MemoryStream();

        const int headerSize = 44;

        // get bit depth
        UInt16 bitDepth = 16; //BitDepth (audioClip);

        // NB: Only supports 16 bit
        //Debug.AssertFormat (bitDepth == 16, "Only converting 16 bit is currently supported. The audio clip data is {0} bit.", bitDepth);

        // total file size = 44 bytes for header format and audioClip.samples * factor due to float to Int16 / sbyte conversion
        int fileSize = audioClip.samples * BlockSize_16Bit + headerSize; // BlockSize (bitDepth)

        // chunk descriptor (riff)
        WriteFileHeader(ref stream, fileSize);
        // file header (fmt)
        WriteFileFormat(ref stream, audioClip.channels, audioClip.frequency, bitDepth);
        // data chunks (data)
        WriteFileData(ref stream, audioClip, bitDepth);

        byte[] bytes = stream.ToArray();

        // Validate total bytes
        Debug.AssertFormat(bytes.Length == fileSize, "Unexpected AudioClip to wav format byte count: {0} == {1}", bytes.Length, fileSize);

        // Save file to persistant storage location
        if (saveAsFile)
        {
            // filepathは引数として読み込むので、コメントアウト
            // filepath = string.Format ("{0}/{1}/{2}.{3}", Application.persistentDataPath, dirname, DateTime.UtcNow.ToString ("yyMMdd-HHmmss-fff"), "wav");
            Directory.CreateDirectory(Path.GetDirectoryName(filepath));
            File.WriteAllBytes(filepath, bytes);
            //Debug.Log ("Auto-saved .wav file: " + filepath);
        }
        else
        {
            // コメントアウト
            // filepath = null;
        }

        stream.Dispose();

        return bytes;
    }
```

# 利用方法
* Unityを起動します。
* Cube等のGameObjectを用意します。
* 用意したGameObjectにDemoAudioRecorder.csをアタッチします。
* Console画面を開き、Playモードに入ります。
* Consoleに表示される指示に従って、マイクに話しかけます。
* 録音が完了したら、filePathに表示されるフォルダを開き、recordedAudio.wavを確認します。
* きちんと録音できていたら、成功です。

# 参考資料
https://github.com/deadlyfingers/UnityWav/tree/master