As The last ship sailed towards the distant horizon I sat there watching on a rock My mind slowly drifting away Forming into my... Dreamtale
Unity - Record Scene sounds
2019-05-04 / 4 min read

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