- Home /
How to do a FFT in Unity?
it's a very simple thing i want: i want to get the spectrum of my audio file without playing it. i want to give an interval of lets say 100ms and get the spectrum of this 100ms for every part of the song without playing it! in short i want to have a GetSpectrumData Function without the "must play" limitation. i want to analyze the whole audio in a short time before i play it!
can't be too difficult right? well i searched hours for a solution - nothing. maybe you guys can help me because i'm completely desperated!
i never asked how to DO a FFT. i asked how to use it or what for but never how to do it. its not my fault if nobody answers i'm desperated and not even my prof can really help me so tell me what i should to this projects is being evaluated!
http://docs.unity3d.com/ScriptReference/AudioSource.GetSpectrumData.html
http://docs.unity3d.com/ScriptReference/FFTWindow.html
float[] data = new float[ 8192 ];
audioSource.GetSpectrumData( data, 0, FFTWindow.BlackmanHarris );
lets quote my own question "in short i want to have a GetSpectrumData Function without the "must play" limitation" getspectrumdata requires the song to play and just gives exactly THIS moment of the song which is playing. to analyze the whole audio i'd have to play the whole audio track before my game begins what'd make a 5 $$anonymous$$ute waiting time. this is inefficient.
i mean if i'd at least have the source code of this function.
what i'm looking for is some kind of function where i can input a float array for the actual samples and another float array that gives the output of the FFT. i tried the NAudio Library but i don't even know what unit they are using for their FFT.
FFTW also uses NAudio. well at least the C# .Net Layer. i found another one on the net but the output wasn't correct too.
so what to do now?
PS: the other one i found is this: http://www.lomont.org/Software/$$anonymous$$isc/FFT/LomontFFT.html but the output wasn't correct. i don't think the -1f to 1f values are correct for this one...
This is exactly the thing I'm looking for did you ever get an answer?
well guess... i finally looked for some ready fft source code and put it into my code without understanding it. well... it worked ^_^
think you could send me the code? drop it in pastebin or something
Hello, now I'm searching for the same solution, Could you please send us the code?
Answer by Bunny83 · Feb 23, 2017 at 02:49 AM
I just implemented the FFT algorithm myself in C#. In addition i first created a Complex number struct which is used by the FFT function. You can find it here on my pastebin. As i mentioned in the info header i basically implemented Paul Bourke's version but instead of using two arrays i used one array of my Complex type. This method calculates the FFT in-place. So it transforms the given sample array (which need to have a length that is a power of two) from the time domain into the frequency domain or the other way round.
A few important notes:
As already mentiond the array always need to have a size that is a power of two (i.e. 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, ...)
If you have your samples as a float array you need to copy them into a Complex array first. Keep in mind that you can reuse the array.
The transformed frequencies reach from 0 up to the sample frequency. However it makes no sense to look at frequencies that are higher than the Nyquist-frequency which is half the sampling frequency. So the second half of the FFT can be ignored. The usual way is to only take the first half and multiply the results by two to compensate the power levels.
Based on the last point it should be clear that if you want for example 1024 frequency bins / bands you need 2048 samples.
If you want to use the FFT class as a filter you should use the whole FFT so when calculating the inverse FFT you get the exact same samples back that you originally feed in. Here are three examples Ex1, Ex2, Ex3. Green is the input signal, red the FFT and yellow the inverse FFT. Those examples have been made from 65k samples. The FFT of 65k (1<<16) samples took around 60ms on my PC. 4k(1<<12) samples took about 3ms
I haven't implemented any windowing functions. So the results are like you used a "rectangle window". If you want / need one you just need to preprocess your data by your desired windowing function.
I've made a quick test to compare Unity's "GetSpectrumData" with my own implementation. The result is pretty much the same:
using B83.MathHelpers;
// [ ... ]
float[] spec = new float[1024];
float[] tmp = new float[2048];
Complex[] spec2 = new Complex[2048];
void Update()
{
// Unity's FFT function
AudioListener.GetSpectrumData(spec, 0, FFTWindow.Rectangular);
for (int i = 0; i < spec.Length; i++)
{
Debug.DrawLine(new Vector3(i, 0), new Vector3(i, spec[i]), Color.cyan);
}
// My FFT based on the output samples.
AudioListener.GetOutputData(tmp, 0);
// copy the output data into the complex array
for(int i = 0; i < tmp.Length; i++)
{
spec2[i] = new Complex(tmp[i],0);
}
// calculate the FFT
FFT.CalculateFFT(spec2, false);
for (int i = 0; i < spec2.Length/2; i++) // plot only the first half
{
// multiply the magnitude of each value by 2
Debug.DrawLine(new Vector3(i, 4), new Vector3(i, 4+(float)spec2[i].magnitude*2), Color.white);
}
}
Of course instead of feeding the FFT function the samples from "AudioListener.GetOutputData" you can also feed it chunks of samples from an audio file. Since the FFT function is static and don't use any global variables / state it can be easily multithreaded if needed. Just ensure that each FFT method / thread has it's own sample array it's working on.
If you're interested in how the FFT (or DFT in general) works i recommend this video. Even though the guy messes up a lot of his math and equations, most are fixed by annotations. What's great about that presentation is the visual representation of what's happening. If you want to make sense of the code i recommend to first look at the DFT implementation (Appendix A.). To make sense of the FFT code you should be familiar with complex numbers. Though you don't have to understand it to use it.
Thanks so much for sharing your code, it helped me a lot! The only thing that was a bit inconvenient is that it had a big GC impact. For that reason I edited the two FFT functions I needed. This resulted in 0 Byte Garbage.
public static void CalculateFFT(Complex[] samples, float[] result, bool reverse)
{
int power = (int)$$anonymous$$ath.Log(samples.Length, 2);
int count = 1;
for (int i = 0; i < power; i++)
count <<= 1;
int mid = count >> 1; // mid = count / 2;
int j = 0;
for (int i = 0; i < count - 1; i++)
{
if (i < j)
{
var tmp = samples[i];
samples[i] = samples[j];
samples[j] = tmp;
}
int k = mid;
while (k <= j)
{
j -= k;
k >>= 1;
}
j += k;
}
Complex r = new Complex(-1, 0);
int l2 = 1;
for (int l = 0; l < power; l++)
{
int l1 = l2;
l2 <<= 1;
Complex r2 = new Complex(1, 0);
for (int n = 0; n < l1; n++)
{
for (int i = n; i < count; i += l2)
{
int i1 = i + l1;
Complex tmp = r2 * samples[i1];
samples[i1] = samples[i] - tmp;
samples[i] += tmp;
}
r2 = r2 * r;
}
r.img = $$anonymous$$ath.Sqrt((1d - r.real) / 2d);
if (!reverse)
r.img = -r.img;
r.real = $$anonymous$$ath.Sqrt((1d + r.real) / 2d);
}
if (!reverse)
{
double scale = 1d / count;
for (int i = 0; i < count; i++)
samples[i] *= scale;
for (int i = 0; i < samples.Length / 2; i++)
{
result[i] = (float)samples[i].magnitude;
}
}
else
{
for (int i = 0; i < samples.Length / 2; i++)
{
result[i] = (float)($$anonymous$$ath.Sign(samples[i].real) * samples[i].magnitude);
}
}
}
public static void Float2Complex(float[] input, Complex[] result)
{
for (int i = 0; i < input.Length; i++)
{
result[i] = new Complex(input[i], 0);
}
}
It also resulted in better performance, so I hope this helps someone too :)
Thank you very much for taking the time to reply in such depth and sharing code. Immensely helpful.
Answer by Ryan-Lintott · Jul 20, 2016 at 02:23 PM
I don't know how to make your own FFT but if you want a hack for using GetSpectrumData without hearing the audio you can do this:
1) Go to the Audio Mixer and add a new group (I called mine "input")
2) Click on the Input group and add the effect Duck Volume
3) In Duck Volume set Make-up Gain all the way to the left and Ratio all the way to the right. Make sure Duck Volume is at the bottom of the stack. This effectively mutes the channel after you've read the spectrum data.
4) On your Audio Source, set the Output to your new group in the Audio Mixer (in my case, "input")
Answer by drudiverse · Oct 01, 2019 at 08:13 AM
Adding to Bunny's awesome code, here's my findings... It may be lacking windows, i.e. rectangular/blackman-harris etc etc. The default window seems to be giving major sound-blurring on the frequency axis, translated by long lines where there is some chirp noise and the algo is not totally sure where the noise is. I know there shouldn't be so many lines as this, will have to work to figure out what it is: higher res: Okay... I have researched the same birdsong from other FFT's, it has frequency axis lines also. these are noise chirps, although I am working with filter banks at the moment which gives me 100 times higher resolution than FFT, so i will use the FFT to optimize the high res scan as a 2nd sweep. nice... https://www.researchgate.net/profile/Valentin_Amrhein/publication/233800034/figure/fig2/AS:300161474547725@1448575480959/Examples-of-nightingale-song-types-containing-elaborate-trills-of-different-repetition.png
Your answer
Follow this Question
Related Questions
Distribute terrain in zones 3 Answers
Is it possible to access the speaker in the DualShock 4 controller without the PS4 kit? 1 Answer
How to import wav file trough script from Project folder? 0 Answers
Playing each channel of a midi track on separate audio sources? 0 Answers
Problem with audio 1 Answer