如何录制Unity 场景中的声音

发布时间: 2022年05月10日阅读数: 2

有些时候我们在游戏想录制游戏内部的声音,那么常用的Unity的MicroPhone API就没什么用了。

好在Unity还提供了OnAudioFilterRead这个API来完成这类需求。

这个方案来自:http://evanxmerz.com/blog/index.php/2016/10/07/recording-in-game-audio-in-unity/

实现步骤:

  • 创建MonoBehaviour,这个脚本需要附到Unity AudioListener 所在的GameObject才能正常工作

  • 在脚本内添加代码

    // 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);
        }
    }
    
  • 将接收到的数据保存到字节流中

    /// 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));
        }
    }
    
  • 保存成声音文件

    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;
    }
    

    其中的AddHeader如下:

    /// 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;
    }
    
  • 使用OnGUI代码测试

    #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