Unity3d sound recording script

Here we provide the code for recording unity3d sound into WAV file. The following script should be attached to audio source. We use onAudioFilterRead() function which gets called every time audio buffer is updated. We convert the buffer to WAV raw data and write the date to the prepared beforehand WAV file. There are two public API function of the script: StartRecording() and FinishRecording(). The wav file name is rec.wav and is hardcoded. Feel free to modify the script to fit your needs.

Note for Android users

The rec.wav is located at Application.persistentDataPath which resolves to /storage/emulated/0/Android/data/<packagename>/files. This folder is unreadable by external applications. To test the WAV created is not corrupted, you need to copy rec.wav to some readable location like Download folder from inside android using native API. This inconvience is caused by Unity3d which provides access only to application files folder and not to public folders on Android.


using System; // for BitConverter
using System.IO; // for FileStream

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class AudioRec : MonoBehaviour
{
    // Sample at standard 44.1 kHz
    private int outputSampleRate = 44100;
    private int headerSize = 44;
    private int bufferSize;
    private int numBuffers;
    private FileStream fileStream;
    private bool recording = false;

    // The function is called after writing the wav file.
    // Format ref: http://soundfile.sapp.org/doc/WaveFormat/
        
    private void WriteHeader()
    {
        fileStream.Seek(0, SeekOrigin.Begin);

        Byte[] riff = System.Text.Encoding.UTF8.GetBytes("RIFF");
        fileStream.Write(riff, 0, 4);

        Byte[] chunkSize = BitConverter.GetBytes(fileStream.Length - 8);
        fileStream.Write(chunkSize, 0, 4);

        Byte[] wave = System.Text.Encoding.UTF8.GetBytes("WAVE");
        fileStream.Write(wave, 0, 4);

        Byte[] fmt = System.Text.Encoding.UTF8.GetBytes("fmt ");
        fileStream.Write(fmt, 0, 4);

        // 16 for PCM; size of the following subchunk
        Byte[] subChunk1 = BitConverter.GetBytes(16); 
        fileStream.Write(subChunk1, 0, 4);


        UInt16 one = 1;
        // PCM = 1
        Byte[] audioFormat = BitConverter.GetBytes(one);
        fileStream.Write(audioFormat, 0, 2);

        UInt16 two = 2;
        // Stereo
        Byte[] numChannels = BitConverter.GetBytes(two);
        fileStream.Write(numChannels, 0, 2);

        // Sample rate 44.1 kHz
        Byte[] sampleRate = BitConverter.GetBytes(outputSampleRate);
        fileStream.Write(sampleRate, 0, 4);

        Byte[] byteRate = BitConverter.GetBytes(outputSampleRate * 4);
        fileStream.Write(byteRate, 0, 4);

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

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

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

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

        fileStream.Flush();
        fileStream.Close();
    }

    private void PrepareWriting()
    {
        string wavFilename = Application.persistentDataPath + "/rec.wav";
        AudioSettings.GetDSPBufferSize(out bufferSize, out numBuffers);

        fileStream = new FileStream(wavFilename, FileMode.Create);
        byte emptyByte = new byte();
        for (int i=0; i<headerSize; i++)
        {
            fileStream.WriteByte(emptyByte);
        }
    }

    public void StartRecording()
    {
        PrepareWriting();
        recording = true;
    }

    public void FinishRecording()
    {
        recording = false;
        WriteHeader();
    }



    void OnAudioFilterRead(float[] data, int channels)
    {        
        if (recording == true) {            
            Int16[] intData = new Int16[data.Length];
            byte[] bytesData = new byte[data.Length * 2];
            int rescaleFactor = 32767;
            
            for (int i = 0; i < data.Length; i++)
            {
                intData[i] = (short)(data[i] * rescaleFactor);
                byte[] byteArr = new byte[2];
                byteArr = BitConverter.GetBytes(intData[i]);
                byteArr.CopyTo(bytesData, i * 2);
            }
            fileStream.Write(bytesData, 0, bytesData.Length);
        } 
    }
}

See also