Anyone know of a code snippet to convert linear-freq FFT into log-freq scale?

The goal is to get impulse responses from a server buffer, FFT it on the client side, and display it with a log frequency scale.

Just did a bit of web searching for how to do this. The problem is that I do not have a solid math or EE background… I was hoping to find somewhere that somebody wrote some linear->log frequency python code that I could translate into SC, but what I find is either a/ hardcore math or b/ “just use this matlab function” with no explanation of what precisely the matlab function is doing.

I’m roughly familiar with the mel-scale triangular window approach but not in any great detail. I took a quick stab at implementing it but the results are unstable. I’m under quite a rush at the moment and I just don’t have time for this – other people have already done this, there’s no good reason for me to waste time re-doing what they already did.

So… halp… somebody must have done this somewhere, I’m just not finding it.

Thanks.
hjh

I am not certain I understand you here. you want to bundle the magnitude per (let’s say) semitone?

I just need the output you need and I have the code somewhere I’m almost certain in FluCoMa - we deal with that in Cpp most of the time but still, the code is there and I like to procrastinate my Saturday todo list :wink:

Thanks – yeah, I had a quick look at the MelBands source code but didn’t really have time to unpack it. Maybe later today.

hjh

I’m hitting a few misunderstandings of the summing here… so I’m inquiring to avoid posting a half-right answer. but I have it working :slight_smile:

Let me know if you prefer the half-right work in progress now

Exactly where I was getting burned… No worries, it was either finish yesterday, or take more time and get it right – didn’t happen yesterday so now there’s no hurry.

Thanks again!
hjh

1 Like

So I got it to work - I’m still investigating something so there is a commented version that is upcoming but for now:

(
b = Buffer.alloc(s,~fftsize);
x = {
	var sig = PinkNoise.ar(0.1);
	FFT(b, sig, 0.25, wintype: 1); // Hann window is recommended for analysis work
	sig.dup;
}.play;
)

(
var lowest = 20;
var highest = 20000;
var numBands = 31;

b.getToFloatArray(2, action: {|bins| //here we dismiss the DC and Nyquist by starting at 2
	var liminalRatio = (highest / lowest).log / numBands;
	var binWidth = s.sampleRate / ~fftsize; //change your SR and your FFT size here
	var mags = Array.fill(numBands,0);
	var nbBins = Array.fill(numBands,0);
	bins.pairsDo{|real, image, idx|
		var whichExpBin = (((idx+1)/2 * binWidth) / lowest).log / liminalRatio;
		if ((whichExpBin >=0) && (whichExpBin < numBands)) {
			mags[whichExpBin] = mags[whichExpBin] + (((real**2) + (image**2))**0.5 / (~fftsize / 4));
			nbBins[whichExpBin] = nbBins[whichExpBin] + 1;
		}
	};
	defer{(mags/nbBins.max(1)).max(0.00001).ampdb.plot};
});
)

Had a go at this – I found that lower frequency (log scale) bins aren’t guaranteed to be populated.

At low frequencies, it’s one linear bin → many log bins. But at high frequencies, multiple linear bins combine into one log bin. The trick seems to be morphing 1:M smoothly over to M:1.

What’s a bit funny about this question is that every EQ plugin shows a frequency analyzer with log frequency scaling, so there is an established technique for this, but when I asked the internet what that is… :cricket::cricket::cricket: (crickets). Usually if it’s a common operation, somebody’s written it up somewhere, but even dsp stackexchange is like, here’s some high level concepts, wish ya luck :face_with_raised_eyebrow:

Keep hacking at it as we can, then…

hjh

Might be worth checking out existing spectrum analyzer code? e.g.

Which also has some links at the top to references

definitely, hence me putting the max(1) in my division :slight_smile:

You can ‘invent’ values there, but there are just any. so people either split the values across the various log bins, or keep them at 0. I did the latter.

if you crank the FFT size to 8192, you get a good resolution at 44100 with this code - which is as true as any FFT-based process for the low end.

or you can determine the lowest adjacent filled bin and put that as your minimum.

Thanks, this is basically what I was looking for, just didn’t find the right magic words to google. Much appreciated.

hjh