Collectives™ on Stack Overflow
Find centralized, trusted content and collaborate around the technologies you use most.
Learn more about Collectives
Teams
Q&A for work
Connect and share knowledge within a single location that is structured and easy to search.
Learn more about Teams
Ask Question
I'm making a game with musical instruments and a recording device in-game. I want the user to be able to play the instruments and record them to an audioclip - and to a wav file - which they can then play back as accompaniment to themselves playing another instrument, the output again recorded, allowing them to make tracks with combined instruments. So far I have everything working except I can't figure out how to get the audioclip - I have .wav files being saved to the asset folder but I don't understand the code enough to figure out where to set the audioclip's data to the data being written.
I'm using code from Olkor here to save the recording as wav, which is working great:
https://unitylist.com/p/za/Output-Audio-Recorder
My two options to save an audioclip from this seem to be a) save to an audioclip at the same time as saving to disk (I can't figure this out) or save it to disk and then load it into the game as an audioclip - using Unity Web Request which I have tried to do but I get an error either cannot access the .audioclip property of an aborted DownloadHandlerAudioClip or if I invoke the function with a delay to load the file, there is an error decoding the audio. Either way I have a saved .wav audio file in my assets folder but no audioclip.
InvalidOperationException: Cannot access the .audioClip property of an aborted DownloadHandlerAudioClip
UnityEngine.Networking.DownloadHandlerAudioClip.GetContent
(UnityEngine.Networking.UnityWebRequest www) (at
C:/buildslave/unity/build/Modules/UnityWebRequestAudio/Public/DownloadHandler
Audio.bindings.cs:49)
OutputAudioRecorder+<GetAudioClip>d__27.MoveNext () (at
Assets/Scripts/OutputAudioRecorder.cs:202)
UnityEngine.SetupCoroutine.InvokeMoveNext (System.Collections.IEnumerator
enumerator, System.IntPtr returnValueAddress) (at C:/buildslave/unity/build/Runtime/Export/Scripting/Coroutines.cs:17)
The script below that I'm using is attached to the audiolistener and so it's picking up all the output of audio from the game. I have looked at doing something with audioListener GetOutputData and passing it to audioClip SetData but I haven't been able to figure it out and I admit that this is beyond my current ability - I'd really appreciate some insight into how to approach this problem in any of the ways possible.
using System;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
public class OutputAudioRecorder : MonoBehaviour
public const string DEFAULT_FILENAME = "record";
public const string FILE_EXTENSION = ".wav";
public bool IsRecording { get { return recOutput; } }
private int bufferSize;
private int numBuffers;
private int outputRate;
private int headerSize = 44; //default for uncompressed wav
private String fileName;
private bool recOutput = false;
private AudioClip newClip;
private FileStream fileStream;
private AudioClip[] audioClips;
private AudioSource[] audioSources;
public int currentSlot;
float[] tempDataSource;
void Awake()
outputRate = AudioSettings.outputSampleRate;
void Start()
AudioSettings.GetDSPBufferSize(out bufferSize, out numBuffers);
audioSources = new AudioSource[3];
audioSources[0] = GameObject.FindWithTag("RecSlot1").GetComponent<AudioSource>();
audioSources[1] = GameObject.FindWithTag("RecSlot2").GetComponent<AudioSource>();
audioSources[2] = GameObject.FindWithTag("RecSlot3").GetComponent<AudioSource>();
public void StartRecording(string recordFileName)
fileName = Path.GetFileNameWithoutExtension(recordFileName) + FILE_EXTENSION;
if (!recOutput)
StartWriting(fileName);
recOutput = true;
Debug.LogError("Recording is in progress already");
public void StopRecording()
recOutput = false;
WriteHeader();
UpdateClip();
private void StartWriting(String name)
fileStream = new FileStream(Application.dataPath + "/" + name, FileMode.Create);
var emptyByte = new byte();
for (int i = 0; i < headerSize; i++) //preparing the header
fileStream.WriteByte(emptyByte);
private void OnAudioFilterRead(float[] data, int channels)
if (recOutput)
ConvertAndWrite(data); //audio data is interlaced
private void ConvertAndWrite(float[] dataSource)
var intData = new Int16[dataSource.Length];
//converting in 2 steps : float[] to Int16[], //then Int16[] to Byte[]
var bytesData = 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 = 32767; //to convert float to Int16
for (var i = 0; i < dataSource.Length; i++)
intData[i] = (Int16)(dataSource[i] * rescaleFactor);
var byteArr = new Byte[2];
byteArr = BitConverter.GetBytes(intData[i]);
byteArr.CopyTo(bytesData, i * 2);
fileStream.Write(bytesData, 0, bytesData.Length);
tempDataSource = new float[dataSource.Length];
tempDataSource = dataSource;
private void WriteHeader()
fileStream.Seek(0, SeekOrigin.Begin);
var riff = System.Text.Encoding.UTF8.GetBytes("RIFF");
fileStream.Write(riff, 0, 4);
var chunkSize = BitConverter.GetBytes(fileStream.Length - 8);
fileStream.Write(chunkSize, 0, 4);
var wave = System.Text.Encoding.UTF8.GetBytes("WAVE");
fileStream.Write(wave, 0, 4);
var fmt = System.Text.Encoding.UTF8.GetBytes("fmt ");
fileStream.Write(fmt, 0, 4);
var subChunk1 = BitConverter.GetBytes(16);
fileStream.Write(subChunk1, 0, 4);
UInt16 two = 2;
UInt16 one = 1;
var audioFormat = BitConverter.GetBytes(one);
fileStream.Write(audioFormat, 0, 2);
var numChannels = BitConverter.GetBytes(two);
fileStream.Write(numChannels, 0, 2);
var sampleRate = BitConverter.GetBytes(outputRate);
fileStream.Write(sampleRate, 0, 4);
var byteRate = BitConverter.GetBytes(outputRate * 4);
fileStream.Write(byteRate, 0, 4);
UInt16 four = 4;
var blockAlign = BitConverter.GetBytes(four);
fileStream.Write(blockAlign, 0, 2);
UInt16 sixteen = 16;
var bitsPerSample = BitConverter.GetBytes(sixteen);
fileStream.Write(bitsPerSample, 0, 2);
var dataString = System.Text.Encoding.UTF8.GetBytes("data");
fileStream.Write(dataString, 0, 4);
var subChunk2 = BitConverter.GetBytes(fileStream.Length - headerSize);
fileStream.Write(subChunk2, 0, 4);
fileStream.Close();
void UpdateClip()
StartCoroutine(GetAudioClip());
IEnumerator GetAudioClip()
using (UnityWebRequest www = UnityWebRequestMultimedia.GetAudioClip("file://" + Application.dataPath + "myRecord1.wav", AudioType.WAV))
yield return www.Send();
if (www.isNetworkError)
Debug.Log(www.error);
AudioClip newClip = DownloadHandlerAudioClip.GetContent(www);
Debug.Log(newClip.name + "name " + newClip.length);
keyboardScript.audioClip = newClip;
–
–
–
Thought this was solution but still not working to load at runtime. (It just refers to the old copy of the clip until I reload the game. Didn't notice this at first.)
While I wasn't able to get the web request to work for me or get WWW to work either, I did get the effect I wanted
by creating a blank clip in the assets folder where the .wav saves, then dragging it in the properties window as a reference, setting the audio clip to streaming and then using the code above to write the wav, it automatically overwrites the clip with the data I recorded.
The audio has a bit of distortion on it (crackling) which I'll look into next.
This solution is I'm aware probably incredibly obvious (i guess I had to give unity something to refer to but didn't realise it) and doesn't solve the problem of loading files at runtime if they aren't predefined like mine are, but it fixed my problem at least.
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.