Unity - Record Scene sounds

Sometimes we just wanna record sounds from scene, so Unity's MicroPhone API would not be very useful.

But we have OnAudioFilterRead to do this kind of action.

Share a solution which is from
http://evanxmerz.com/blog/index.php/2016/10/07/recording-in-game-audio-in-unity/

This script should be added to a Unity AudioListener GameObject then it could do its works.

HowTo

  • Create a MonoBehaviour script with an OnAudioFilterRead method
    // write the incoming audio to the output string
    void OnAudioFilterRead(float[] data, int channels)
    {
    
    if (this.Rendering)
    {
        // store the number of channels we are rendering
        this.channels = channels;

        // store the data stream
        this.Write(data);
    }
}
```  
  • Save the received data into the byte stream

    /// Write a chunk of data to the output stream.
    public void Write(float[] audioData)
    {
        // Convert numeric audio data to bytes
        for (int i = 0; i < audioData.Length; i++)
        {
            // write the short to the stream
            this.outputWriter.Write((short) (audioData[i] * (float) Int16.MaxValue));
        }
    }
    
  • Save the stream with a WAV format header

    public AudioRenderer.Result Save(string filename)
    {
        Result result = new AudioRenderer.Result();
    
        if (outputStream.Length > 0)
        {
            // add a header to the file so we can send it to the SoundPlayer
            this.AddHeader();
    
            // if a filename was passed in
            if (filename.Length > 0)
            {
                // Save to a file. Print a warning if overwriting a file.
                if (File.Exists(filename))
                    Debug.LogWarning("Overwriting " + filename + "...");
    
                // reset the stream pointer to the beginning of the stream
                outputStream.Position = 0;
    
                // write the stream to a file
                FileStream fs = File.OpenWrite(filename);
    
                this.outputStream.WriteTo(fs);
    
                fs.Close();
    
                // for debugging only
                Debug.Log("Finished saving to " + filename + ".");
            }
    
            result.State = Status.SUCCESS;
        }
        else
        {
            Debug.LogWarning("There is no audio data to save!");
    
            result.State = Status.FAIL;
            result.Message = "There is no audio data to save!";
        }
    
        return result;
    }
    

    And add headers

    /// This generates a simple header for a canonical wave file, 
    /// which is the simplest practical audio file format. It
    /// writes the header and the audio file to a new stream, then
    /// moves the reference to that stream.
    /// 
    /// See this page for details on canonical wave files: 
    /// http://www.lightlink.com/tjweber/StripWav/Canon.html
    private void AddHeader()
    {
        // reset the output stream
        outputStream.Position = 0;
    
        // calculate the number of samples in the data chunk
        long numberOfSamples = outputStream.Length / (BITS_PER_SAMPLE / 8);
    
        // create a new MemoryStream that will have both the audio data AND the header
        MemoryStream newOutputStream = new MemoryStream();
        BinaryWriter writer = new BinaryWriter(newOutputStream);
    
        writer.Write(0x46464952); // "RIFF" in ASCII
    
        // write the number of bytes in the entire file
        writer.Write((int) (HEADER_SIZE + (numberOfSamples * BITS_PER_SAMPLE * channels / 8)) - 8);
    
        writer.Write(0x45564157); // "WAVE" in ASCII
        writer.Write(0x20746d66); // "fmt " in ASCII
        writer.Write(16);
    
        // write the format tag. 1 = PCM
        writer.Write((short) 1);
    
        // write the number of channels.
        writer.Write((short) channels);
    
        // write the sample rate. 44100 in this case. The number of audio samples per second
        writer.Write(SAMPLE_RATE);
    
        writer.Write(SAMPLE_RATE * channels * (BITS_PER_SAMPLE / 8));
        writer.Write((short) (channels * (BITS_PER_SAMPLE / 8)));
    
        // 16 bits per sample
        writer.Write(BITS_PER_SAMPLE);
    
        // "data" in ASCII. Start the data chunk.
        writer.Write(0x61746164);
    
        // write the number of bytes in the data portion
        writer.Write((int) (numberOfSamples * BITS_PER_SAMPLE * channels / 8));
    
        // copy over the actual audio data
        this.outputStream.WriteTo(newOutputStream);
    
        // move the reference to the new stream
        this.outputStream = newOutputStream;
    }
    
  • Test with OnGUI codes

    #if UNITY_EDITOR
    void OnGUI()
    {
        if (GUI.Button(new Rect(0, 0, 100, 100), "Start"))
        {
            this.Rendering = true;
        }
    
        if (GUI.Button(new Rect(0, 110, 100, 100), "Stop"))
        {
            this.Rendering = false;
            Save(Application.dataPath + "/" + "234.wav");
        }
    }
    #endif
    
WRITTEN BY:    Suntabu