- Michal Wallace wrote:
> What I tried to do was create 10 seconds of fake data,

Here we go ...

> simulating "pure" brainwaves (such as a steady 6Hz for

> theta), as well as some contrived mixtures. Then, I

> ran an FFT on them to get the spectrogram.

>

> The output bears some resemblance to what I wanted, but

> it's grossly distorted, and I'm not sure what's wrong.

>

> I think the code *MIGHT* be working fine except that it's

> using the wrong scale for the "bins", because it looks like

> it might be right, except it's all squished into the top

> bars and there seems to be a lot of noise.

When you do an FFT of a block of sound, you can't just FFT it straight

off. Well, you can, but you end up with lots of distortion because

you have effectively cut a rectangle out of the sound, and there are

big clicks at the start and end of it. (And when you give a chunk of

sound to an FFT, it acts as if that sound chunk is looped forever, so

you can imagine what kind of distortion that creates if you don't tidy

up the edges).

So, you need to apply a 'window' to the data before FFTing it. If you

look at BWView, if you click somewhere on the display, you can see the

'window function' being used in the display above. This looks

something like a bell-shape.

So, if you want nice results from your FFT, you need to window the

data first -- which means multiplying it by a 'window function',

effectively flattening off the edges. Don't think you can choose just

any old function, because each different shape has its own particular

frequency response. There are several types of window, but I used the

'Blackman' window in my code.

You mention that you've already looked at the code, so you should have

no trouble finding this (in analysis.c around line 410 onwards). The

multiplier is given by this:

double mag= 0.42 + 0.5 * cos(ang) + 0.08 * cos(2*ang); // Blackman window

Where 'ang' goes from -pi at one end of the chunk to +pi at the other.

It's a while since I've coded in Python, so I'll prototype it in C for

you:

for (a= 0; a<64; a++) {

double ang= (a + 0.5 - 32.0) * M_PI / 32.0;

double mag= 0.42 + 0.5 * cos(ang) + 0.08 * cos(2*ang);

slice[a] *= mag;

}

I think that should be correct.

Note that a 6Hz wave is not going to be very easy to see if you are

only using a 0.25 second window (that's only 1.5 of a wavelength

you're expecting it to recognise!). Bear in mind that the lowest band

in your FFT will be centred on 0Hz, then 4Hz, then 8Hz, 16Hz, 24Hz,

and so on up to 128Hz.

Jim

--

Jim Peters (_)/=\~/_(_) jim@...

(_) /=\ ~/_ (_)

Uazú (_) /=\ ~/_ (_) http://

B'ham, UK (_) ____ /=\ ____ ~/_ ____ (_) uazu.net - On Mon, 17 Jun 2002, larryjanow wrote:

> --- In buildcheapeeg@y..., Jim Peters <jim@u...> wrote:

Larry: I was wondering how I'd figure out amplitude. Thanks! :)

>

> > If real_fft is returning 33 values from a 64-sample input, then it

> > seems to me that it must be returning complex numbers. If so, then

> > the abs() function should automatically do the square root you

> > mention (i.e. sqrt(r*r+i*i)).

> >

> > Perhaps this needs checking. However, obviously the Python

> > FFT.real_fft is not returning the imaginary parts as real numbers

> > in the places you are expecting it to, so your method won't work in

> > this particular case, and is based on some other FFT algorithm's output

> > layout.

>

> I made my comments based on Michal's statement about discarding the

> imaginary bins and just using the real ones. While my example might

> not work for his FFT's output format, the method is still valid:

> a=sqrt(r*r+i*i)/(N/2). If the compiler or library handles this (I

> know nothing about Python...), that is cool!

Here's the deal. The FFT module has several functions avaliable:

http://pfdubois.com/numpy/doc/FFT/FFT.py.html

I used real_fft because the sound processing in python

article said the real and imaginary parts would be mirror

images. I didn't check that assertion or try to see if it

worked in my case. I just chopped it off and saw that the

right thing seemed to be happening. But there's a plain

fft() function that returns a list of the same length as the

input.

I should say that complex numbers are a primitive type in

python, so the fft() function returns an array of complex

numbers, and you can take the .imaginary attribute or the

.real attribute to get an array with only the axis you want.

I've got some work-related stuff I need to get done today,

but I should have some time to play around with this

tomorrow.

If anyone wants to tak a look in the meantime, be my guest.

Cheers,

- Michal http://www.sabren.net/ sabren@...

------------------------------------------------------------

Switch to Cornerhost! http://www.cornerhost.com/

High Powered Hosting - With a Human Touch. :)

------------------------------------------------------------