- Home /
Recording Audio with OnAudioFilterRead, but resulting file doesn't work.
Hello everyone,
I have several audio-sources (different musical instruments) playing at the same time, for a mixing purpose. And I have my script attached on my AudioListner.
Scipt was originally written by Gregzo (the auther of the plugin G-Audio), but this is an old scipt, however it worked for other users.
I am able to create a wav file, but no player can play the file, and can't figure out what might be reason.
Here's the code attached to the AudioListener
private var bufferSize : int;
private var numBuffers : int;
private var outputRate : int = 44100;
private var fileName : String = "recTest.wav";
private var headerSize : int = 44; //default for uncompressed wav
private var recOutput : boolean;
private var fileStream : FileStream;
function Awake()
{
AudioSettings.outputSampleRate = outputRate;
}
function Start()
{
AudioSettings.GetDSPBufferSize(bufferSize,numBuffers);
}
public var obj : GameObject ;
function Update()
{
if(Input.GetKeyDown("r"))
{
if(recOutput == false)
{
StartWriting(fileName);
recOutput = true;
}
else
{
recOutput = false;
WriteHeader();
print("rec stop");
}
}
}
function StartWriting(name : String)
{
fileStream = new FileStream(name, FileMode.Create);
var emptyByte : byte = new byte();
for(var i : int = 0; i<headerSize; i++) //preparing the header
{
fileStream.WriteByte(emptyByte);
}
}
function OnAudioFilterRead(data : float[], channels : int)
{
if(recOutput)
ConvertAndWrite(data); //audio data is interlaced
}
function ConvertAndWrite(dataSource : float[])
{
var intData : Int16[] = new Int16[dataSource.length];
//converting in 2 steps : float[] to Int16[], //then Int16[] to Byte[]
var bytesData : Byte[] = new Byte[dataSource.length*2];
//bytesData array is twice the size of
//dataSource array because a float converted in Int16 is 2 bytes.
var rescaleFactor : int = 32767; //to convert float to Int16
for (var i : int = 0; i<dataSource.length;i++)
{
intData[i] = dataSource[i]*rescaleFactor;
var byteArr : Byte[] = new Byte[2];
byteArr = BitConverter.GetBytes(intData[i]);
byteArr.CopyTo(bytesData,i*2);
}
fileStream.Write(bytesData,0,bytesData.length);
}
function WriteHeader()
{
// fileStream.Seek(0,Application.dataPath);
fileStream.Seek(0,SeekOrigin.Begin);
var riff : Byte[] = System.Text.Encoding.UTF8.GetBytes("RIFF");
fileStream.Write(riff,0,4);
var chunkSize : Byte[] = BitConverter.GetBytes(fileStream.Length-8);
fileStream.Write(chunkSize,0,4);
var wave : Byte[] = System.Text.Encoding.UTF8.GetBytes("WAVE");
fileStream.Write(wave,0,4);
var fmt : Byte[] = System.Text.Encoding.UTF8.GetBytes("fmt");
fileStream.Write(fmt,0,4);
var subChunk1 : Byte[] = BitConverter.GetBytes(16);
fileStream.Write(subChunk1,0,4);
var two : UInt16 = 2;
var one : UInt16 = 1;
var audioFormat : Byte[] = BitConverter.GetBytes(one);
fileStream.Write(audioFormat,0,2);
var numChannels : Byte[] = BitConverter.GetBytes(two);
fileStream.Write(numChannels,0,2);
var sampleRate : Byte[] = BitConverter.GetBytes(outputRate);
fileStream.Write(sampleRate,0,4);
var byteRate : Byte[] = BitConverter.GetBytes(outputRate*4);
// sampleRate * bytesPerSample*number of channels, here 44100*2*2
fileStream.Write(byteRate,0,4);
var four : UInt16 = 4;
var blockAlign : Byte[] = BitConverter.GetBytes(four);
fileStream.Write(blockAlign,0,2);
var sixteen : UInt16 = 16;
var bitsPerSample : Byte[] = BitConverter.GetBytes(sixteen);
fileStream.Write(bitsPerSample,0,2);
var dataString : Byte[] = System.Text.Encoding.UTF8.GetBytes("data");
fileStream.Write(dataString,0,4);
var subChunk2 : Byte[] = BitConverter.GetBytes(fileStream.Length-headerSize);
fileStream.Write(subChunk2,0,4);
fileStream.Close();
}
Thank You for your time
Answer by Nerevar · Jul 15, 2014 at 12:39 PM
Hello,
I managed to read this file with Audacity (import raw data)
If you want to use another player you might need some extra plugin or codec using FFmpeg library
In unity I don't know, I thought you just needed to read it outside unity.
Unity does not recognize this wav format so you need to record to a format unity undersands.
You can convert it with audacity so it can be read by unity afterwards but it is not a solution if you need that at runtime :p
I was able to read the file by installing FFmpeg library then importing as a Raw file (import Raw), with Audacity.
This means the file is not corrupter, just not the encoding W$$anonymous$$P/VLC .. accept.
thanks for the hint
I keep working on making a format usable for unity, I try to modify the code, I keep you informed.
many thanks, you mean for now I can't export/create an audio file that will be readable by mobile devices?
All I need to to record the playing audio and saving anywhere on the device.
Answer by ShawnFeatherly · Jun 05, 2019 at 09:31 PM
This blog post included adding a header to the *.wav file: http://evanxmerz.com/blog/index.php/2016/10/07/recording-in-game-audio-in-unity/
Here's a full copy of the code on that page, just in case it goes down:
using UnityEngine;
using System;
using System.IO;
public class AudioRenderer : MonoBehaviour
{
#region Fields, Properties, and Inner Classes
// constants for the wave file header
private const int HEADER_SIZE = 44;
private const short BITS_PER_SAMPLE = 16;
private const int SAMPLE_RATE = 44100;
// the number of audio channels in the output file
private int channels = 2;
// the audio stream instance
private MemoryStream outputStream;
private BinaryWriter outputWriter;
// should this object be rendering to the output stream?
public bool Rendering = false;
/// The status of a render
public enum Status
{
UNKNOWN,
SUCCESS,
FAIL,
ASYNC
}
/// The result of a render.
public class Result
{
public Status State;
public string Message;
public Result(Status newState = Status.UNKNOWN, string newMessage = "")
{
this.State = newState;
this.Message = newMessage;
}
}
#endregion
public AudioRenderer()
{
this.Clear();
}
// reset the renderer
public void Clear()
{
this.outputStream = new MemoryStream();
this.outputWriter = new BinaryWriter(outputStream);
}
/// 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));
}
}
// 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);
}
}
#region File I/O
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;
}
/// 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;
}
#endregion
}
I came upon this code just a few days back and used it in my project. The wav file is created fine but there are some errors. On my android phone, only Poweramp player could play the file, and on PC I checked on AI$$anonymous$$P player, there is a mismatch in actual duration and stated (double of actual) duration in the file info. I cross-checked and tallied the numbers put by the Add Header code, and found that the header states the total number of bytes as double the actual amount. For a quick check I just divided that by 2 and result wav file played successfully on my phone's native music app, on which it failed to play earlier, but still not all players are playing the file (for e.g. ES media player). Any suggestions?
Now, ins$$anonymous$$d of this
writer.Write((int)(HEADER_SIZE + (numberOfSamples * BITS_PER_SA$$anonymous$$PLE * channels / 8)) - 8);
I just did this
writer.Write((int)outputStream.Length-8);
And ins$$anonymous$$d of
writer.Write((int)(numberOfSamples * BITS_PER_SA$$anonymous$$PLE * channels / 8));
did this
writer.Write((int)outputStream.Length-44);
Now there's no discrepancy in the durations, but all media players played successfully except the ES $$anonymous$$edia Player.
Your answer
![](https://koobas.hobune.stream/wayback/20220613154121im_/https://answers.unity.com/themes/thub/images/avi.jpg)