akyboxブログ

旧AkiIroブログ

【Unity】AudioListerを録音してwavにする

録音する

Unity内で鳴っている音をそのまま録音します.
ソースコードはこちら.

using System;
using System.IO;
using System.Text;
using UnityEngine;

[RequireComponent(typeof(AudioListener))]
public class AudioListener2Wav : MonoBehaviour {
    private int m_outputRate = 44100;
    private bool m_isRecording = false;
    private FileStream m_stream;
    readonly private string m_fileName = "recTest.wav";
    readonly private int m_headerSize = 44;
    float[] wave = new float[1024];

    void Update ()
    {
        if (!Input.GetKeyDown(KeyCode.R)) return;
        if (m_isRecording == false)
        {
            Debug.Log("rec started",this);
            m_isRecording = true;
            startWriting(m_fileName);
        }
        else {
            m_isRecording = false;
            writeHeader();
            Debug.Log("rec stop", this);
        }
    }

    private void OnAudioFilterRead(float[] data, int channels)
    {
        if (!m_isRecording) return;
        convertAndWrite(data);
    }

    /// <summary>
    /// ストリームを0埋めして初期化
    /// </summary>
    /// <param name="name">ファイルの名前</param>
    private void startWriting(string name)
    {
        m_stream = new FileStream(name, FileMode.Create);
        var emptyByte = new byte();
        for (int i = 0; i < m_headerSize; i++) {
            m_stream.WriteByte(emptyByte);
        } 
    }

    /// <summary>
    /// 変換してストリームに書き込む
    /// </summary>
    /// <param name="dataSource">書き込むデータ</param>
    private void convertAndWrite(float[] dataSource)
    {
        Int16[] intData = new Int16[dataSource.Length];
        var bytesData = new byte[dataSource.Length*2];
        int rescaleFactor = 32767;
        for (int i = 0; i < dataSource.Length; i++) {
            intData[i] = (short) (dataSource[i] * rescaleFactor);
            var byteArr = new byte[2];
            byteArr = BitConverter.GetBytes(intData[i]);
            byteArr.CopyTo(bytesData, i * 2);
        }
        m_stream.Write(bytesData, 0, bytesData.Length);
    }

    /// <summary>
    /// ヘッダを書く
    /// 詳しくはwavのフォーマットを確認
    /// http://www.graffiti.jp/pc/p030506a.htm
    /// </summary>
    private void writeHeader()
    {
        m_stream.Seek(0, SeekOrigin.Begin);
        
        Byte[] riff = Encoding.UTF8.GetBytes("RIFF");
        m_stream.Write(riff, 0, 4);
        Byte[] chunkSize = BitConverter.GetBytes(m_stream.Length - 8);
        m_stream.Write(chunkSize, 0, 4);
        Byte[] wave = Encoding.UTF8.GetBytes("WAVE");
        m_stream.Write(wave, 0, 4);
        Byte[] fmt = Encoding.UTF8.GetBytes("fmt ");
        m_stream.Write(fmt, 0, 4);
        Byte[] subChunk1 = BitConverter.GetBytes(16);
        m_stream.Write(subChunk1, 0, 4);

        UInt16 one = 1;
        UInt16 two = 2;
        Byte[] audioFormat = BitConverter.GetBytes(one);
        m_stream.Write(audioFormat, 0, 2);
        Byte[] numChannels = BitConverter.GetBytes(two);
        m_stream.Write(numChannels, 0, 2);

        Byte[] sampleRate = BitConverter.GetBytes(m_outputRate);
        m_stream.Write(sampleRate, 0, 4);

        Byte[] byteRate = BitConverter.GetBytes(m_outputRate * 4);
        // sampleRate * bytesPerSample*number of channels, here 44100*2*2
        m_stream.Write(byteRate, 0, 4);

        UInt16 four = 4;
        Byte[] blockAlign = BitConverter.GetBytes(four);
        m_stream.Write(blockAlign, 0, 2);

        UInt16 sixteen = 16;
        Byte[] bitPerSample = BitConverter.GetBytes(sixteen);
        m_stream.Write(bitPerSample, 0, 2);

        Byte[] dataString = Encoding.UTF8.GetBytes("data");
        m_stream.Write(dataString, 0, 4);

        Byte[] subChunk2 = BitConverter.GetBytes(m_stream.Length - m_headerSize);
        m_stream.Write(subChunk2, 0, 4);

        m_stream.Close();
    }
}

同じGameObjectにAudioListenerがいないと
OnAudioFilterReadがコールされません.(ハマりました)

使い方

  1. AudioListerのついたGameObjectにアタッチ
  2. Rボタンを叩く(録画開始)
  3. 再度Rボタンを叩く(録画終了)

参考

参考にした元のソースはこちらにあります. ただしこちらはUnityScriptです.
https://forum.unity3d.com/threads/writing-audiolistener-getoutputdata-to-wav-problem.119295/