- Home /
Real time Microphone (line input) FFT analysis
Hi all, so I've implemented a decent FFT analysis in code, but I don't know how to get realtime mic /line audio into unity.
I see that there is a Microphone class that has some basic functionality, but in playing with it so far there doesn't seem to be any way to pipe it into the scene to be picked up by the AudioListener (which is where my FFT analysis happens with AudioListener.GetSpectrumData() ).
I've looked around on the forums and here, and all I see are questions circa 2010- mid 2012 with no answers. Is there more recent info on this? Has anyone gotten this to work? Are there other solutions I should look at, such as fMod? I need to get this working, and short of having a 2nd program do audio capture and fill an audio buffer that then gets read into OnAudioFilterRead (which is beyond the scope of my time and knowledge at the moment), I'm at a loss of what to do.
Help?
Answer by zombience · Aug 04, 2013 at 10:24 PM
this is the answer I've currently got going. would have posted as a comment, but couldn't post this much code.
ah, hmm. i'm trying to get down as close to real time as possible for the purposes of music visualization. i realize unity isn't really built for that, so i'm kinda trying to just work with what I can.
using this code, I've got down to about 50 milliseconds, but there's a bit of a stutter every time the buffer switches. but in this case the audio only needs to be analyzed, not actually heard, but it does seem to choke the game in that moment.
This is the entire script I created to make this happen. I haven't revisited it to make it cleaner, just set it up to work.
Also, if you're looking for non-realtime audio analysis (e.g. running audio analysis on saved audio files that are playing back), this script works well with very little latency
using UnityEngine;
using System.Collections;
public class FFT : MonoBehaviour
{
#region vars
public string CurrentAudioInput;
private AudioObj[] audioObj = new AudioObj[2];
private const int BANDS = 4;
//public float[] curve = new float[BANDS]; //scale output of band analysis
public float[] output = new float[BANDS];
public string[] inputDevices;
private int[] crossovers = new int[BANDS];
private float[] freqData = new float[8192];
private float[] band;
public GameObject playerPrefab;
private int index = 0;
public static FFT Instance;
private bool doSound = true;
private int deviceNum;
private struct AudioObj
{
public GameObject player;
public AudioClip clip;
public void SetClip(AudioClip c)
{
clip = c;
player.audio.clip = c;
/*
slowing the playback down a small amount allows enough space between
recording and output so that analysis does not overtake the recording.
this helps with stutter and distortion, but doesn't solve it completely
*/
player.audio.pitch = .95f;
}
}
#endregion
#region Unity Methods
void Start()
{
Instance = this;
crossovers[0] = 30; //guesstimating sample lengths for frequency bands
crossovers[1] = 50;
crossovers[2] = 600;
crossovers[3] = freqData.Length;
band = new float[BANDS];
output = new float[BANDS];
for (int i = 0; i < audioObj.Length; i++)
{
audioObj[i].player = (GameObject)Instantiate(playerPrefab);
audioObj[i].player.transform.parent = transform;
audioObj[i].player.transform.position = Vector3.zero;
audioObj[i].clip = new AudioClip();
}
inputDevices = new string[Microphone.devices.Length];
deviceNum = Microphone.devices.Length - 1;
for (int i = 0; i < Microphone.devices.Length; i++)
inputDevices[i] = Microphone.devices[i].ToString();
CurrentAudioInput = Microphone.devices[deviceNum].ToString();
InvokeRepeating("Check", 0, 1.0f / 15.0f);
StartCoroutine(StartRecord());
}
void Update()
{
KeyInput();
}
#endregion
#region Actions
private void Check()
{
if (!doSound)
return;
audioObj[index].player.audio.GetSpectrumData(freqData, 0, FFTWindow.Hamming);
bool cutoff = false;
int k = 0;
float[] lengths = new float[BANDS];
for(int i = 0; i < BANDS; i++)
{
float min = (i > 0 ? crossovers[i-1] : 0);
lengths[i] = crossovers[i] - min;
band[i] = 0f;
}
for (int i = 0; i < freqData.Length; i++)
{
if (k > BANDS - 1)
break;
band[k] += freqData[i];
if(i > crossovers[k])
{
output[k] = Mathf.Abs(band[k] / lengths[k]);
k++;
}
if (i > crossovers[BANDS - 1] - 10)
cutoff = true;
}
}
private IEnumerator StartRecord()
{
audioObj[index].clip = Microphone.Start(Microphone.devices[deviceNum], true, 5, 24000);
/*
the longer the mic recording time, the less often there are "hiccups" in game performance
but also due to being pitched down, the playback gradually falls farther behind the recording
*/
print("recording to audioObj " + index);
StartCoroutine(StartPlay(audioObj[index].clip));
yield return new WaitForSeconds(5);
StartCoroutine(StartRecord()); //swaps audio buffers, begins recording and playback of new buffer
/* it is necessary to swap buffers, otherwise the audioclip quickly becomes too large and begins to slow down the system */
}
private IEnumerator StartPlay(AudioClip buffer)
{
audioObj[index].SetClip(buffer);
yield return new WaitForSeconds(.01f);
audioObj[index].player.SetActive(true);
audioObj[index].player.audio.Play();
audioObj[Mathf.Abs((index % 2) - 1)].player.audio.Stop();
index++;
if (index > 1)
index = 0;
}
private void KeyInput()
{
if (Input.GetKeyDown(KeyCode.A))
{
doSound = !doSound;
}
if (Input.GetKeyDown(KeyCode.Equals))
{
deviceNum++;
if (deviceNum > Microphone.devices.Length - 1)
deviceNum = 0;
CurrentAudioInput = Microphone.devices[deviceNum].ToString();
}
}
#endregion
}
Hi zombience. Would it be possible to use your code to find the frequency of a note played for use in a guitar tuner? And how would you go about it? I have implemented an FFT based on this tutorial I came across that was really helpful but it is not quite accurate enough for what I need.
There are some similarities between the code I have and yours but yours is way more complex than $$anonymous$$e, so I'm guessing it might estimate the frequency more accurately, I'm just too much of a novice to figure it all out!
Thanks for any help! :-)
Ping @zombience. I'd be curious to check out your code. Could you post it up into a repo on Github? It sounds like people have been refining the original script for some time now. So having it there, might be a good way to keep the progress going and open.
@robksawyer hey there, looks like people are interested ^_^ i've been pretty busy, but i'll put up some code on a repo somewhere. i'd actually really appreciate the help.
Full disclosure: what i'm doing is not actually true FFT analysis, but rather just gathering all the frequency bands from GetSpectrumData, which has an ordered array of floats representing all frequencies from low to max. I'm then just chunking them into four groups and measuring the energy (amplitude) of each group. I'm not even doing anything so fancy as windowing / fading crossovers between frequency groups, so it's pretty rough.
@Philsko if you actually display the individual amplitude of each value co$$anonymous$$g out of GetSpectrumData, you can in fact find out frequencies (and overtones) that way. But my math here doesn't translate float value to real-world frequency value. I'm unfortunately really terrible at math, so I'm not sure I can make that leap.
I'll upload the code in the next week or so and hopefully having some other eyes on it could turn it into a good solid analysis project ^_^
hey all, sorry for the delay, this has been a low priority as i've had a lot else going on. i'll try to get something up on this soon, possibly this week.
to clarify a few things: 1) the package i've got going isn't too different from what is up above, although it is simplified a bit, and also has a few modules for controlling basic things in unity (such as scale, color, position based on amplitude)
2) it isn't true FFT, it's just measuring the amplitude of 4 crudely split bands (without even any windowing) and sending that info out as a float
3) because it isn't true FFT and i've just "made it work", there are "magic numbers" that i've just found to work best, and aren't based on any math knowledge on my part.
hey all, sorry it has taken so long to get this going.
you can now view the project on github here:
https://github.com/zombience/audio_analyzer
i've stripped it down to the basics, so hopefully there shouldn't be too much to wade through to get started. there is an example scene with everything working.
if anyone has a better way or more accurate method of analyzing audio, send a pull request. i would definitely appreciate a more knowledgeable fft guru to step in and meddle with it ^_^
here is an example of something i did a ways back using this package: https://www.youtube.com/watch?v=kyc5T5X274s
Answer by DaveA · May 02, 2013 at 10:55 PM
This works fine. What I do is set the Microphone to loop and set it as the AudioClip on an AudioSource, then use the spectrum data off the AudioSource, rather than listener.
I can't seem to get this to work. I have confirmed that the mic is set to loop and is being set as the clip for the audiosource, but the FFT data seems to be garbage, and doesn't matter whether I'm analyzing the AudioClip itself or the AudioSource.
Can you explain further, or is there anything I should be looking at?
Use the code here http://docs.unity3d.com/Documentation/ScriptReference/AudioSource.GetSpectrumData.html to visualize it. I set the length to 1 (which I think is a bug that it can't be smaller), but produces 'only' 1 second delay. $$anonymous$$aybe you can post your code? Note I have best results on PC, Android is less good.
had to add it as a new answer to cover that much code. it's down below
I made some improvements/changes to what you posted, DaveA:
https://gist.github.com/andykorth/bf8e4509402c1f090c1c
A few notes: I only use one AudioClip buffer, haven't seen it necessary to change, since they record in a circular loop. To choose where you want GetSpectrumData to sample, you can set the audioSource's time, and as long as it's playing, you can get the spectrum data. Right now, I'm backing up 30 ms and running from there. This should probably be an amount based on the window size and how often you want to run it, etc..
Specifically, I'm getting the microphone's current time and going back 30 ms. Probably I should handle the looping situation, but I haven't done that yet.
The net effect is a lot less code and doesn't require lots of short coroutines.
Looks like test project doesn't work properly =( Not with microphone not with audioclip
Answer by MarkD · Mar 30, 2014 at 01:16 PM
I know this one has been answered but have you seen this one on the asset store, its a free script https://www.assetstore.unity3d.com/#/content/12518
ah, i haven't seen it. i'll check out what they're up to. thanks for the info! if it works well, i'll probably tinker with the code to get a multiband version of it, since it appears to be only an overall loudness meter.
thanks for the info!
np, I wrote that script, you should be able to get sufficient data for multi-banding out of it (once you add your own code). If not feel free to contact me trough the support link in the store.
good luck
Hi I can't find the problem online. The $$anonymous$$ic Control script freezes Unity on press play. Both C# and Javascript. I'm on Unity 4.3, Windows 7 1. Create an Empty 2. Drag the script on 3. press play
it freezes. just to test, I created an audio source to use it for the Javascript version. Still freezes
There is a newer version online, that was a bug in one of the first builds and was fixed a few versions ago. Check it out on the asset store.
Also make sure you are not running in mobile mode, as that uses a different way of mic input, it will freeze up.
I did download the version I'm using a couple of days ago from the assets store. I did use the Javascript version, which apparently is not updated anymore. I don't know any C#, so using this version would be a lot easier. How can I change the mode I'm in. The last export I used was web player and the built settings are set to that, but if I click on something else they get changed back, unless I actually build the project with different settings. Just in case this matters, I am using a USB microphone.
Hey, I checked out your package a while back and I've implemented my own solution somewhat based on your package.
I've included multiband amplitude, curve values to amplify or attenuate each band, moveable bands, and a static array of values to access from any script.
i'm currently using this to drive a much larger audio interaction script that can be used to get amplitude data (which currently is reasonably fast, i'd guess around 10-15 ms of latency) and drive various events for a unity visualizer that i've been using out at dj events and in performance art situations.
when i've gone a bit further in refining it, i'll post the code somewhere.
until then... it's co$$anonymous$$g. and thank you for pointing the way with some basic info on how to get it done! ^_^
Hi Zombience,
Just wondering where you got to with this and if your still happy to post the code?
Cheers - James
Answer by robksawyer · May 07, 2015 at 05:18 PM
I've posted the code from the original answer (with some tidying) up at https://github.com/robksawyer/Unity-Live-Input-FFT-Analysis.