0
$\begingroup$

I'm working on a spectral processing plugin that's predicated on allowing the user to manipulate per-bin values in an STFT algorithm. I normalize the bin amplitudes by diving them by $N$ (number of samples used in the DFT), but the resulting numbers often inhabit a range that is difficult to work with directly, e.g. it is very difficulty to precisely gate/limit an amplitude value that is often well below 0.1.

Obviously, once normalized, each DFT bin has an absolute amplitude range of 0-1, but what I'd like to do is scale them somehow so they tend to span that entire range, factoring in things specific to audio signals like the bass content often being much greater than the treble. My first instinct is to either apply some logarithmic curve, or maybe even use a table of values derived from the Fletcher-Munson curve, but I wanted to ask here and see if I couldn't go about it in a smarter way. Thank you!

EDIT: I believe I might not have explained my intentions well enough! The normalization I'm planning on doing is going to be temporary, so that the user can easily set values without needing to work with tiny decimal places. E.G., the user can set a "gate" level of 0.3, and then all bins with a "normalized" amplitude under 0.3 would be muted. What I'd like is for a single gate value to work reasonably well for all frequencies, even though lower-frequency bins tend to have a much amplitude in most musical signals.

$\endgroup$
5
  • $\begingroup$ Two questions (for now): 1. What are you trying to do with the STFT? 2. What is the frame width and the frame hop distance? Maybe a third question is what window are you using for analysis? $\endgroup$ Apr 6, 2022 at 3:18
  • $\begingroup$ @robertbristow-johnson The STFT is being used for phase vocoding, and i'm using variable frame sizes between 64-2048, usually with a hop size of N/4 or N/8, and I believe I'm using a hanning window. The STFT is working fine, I'm just look for a good way to represent the bin amplitudes so they're easy to work with in a GUI! $\endgroup$ Apr 7, 2022 at 2:41
  • $\begingroup$ How are you overlapping a frame of size 2048 with a frame of size 1024? Or a frame of size 64? I think, if the frame windows are not complementary (that is that they add to 1) you're gonna have problems. What are you doing with the phase vocoder? Time-scaling? $\endgroup$ Apr 7, 2022 at 3:31
  • $\begingroup$ Oh I think I misspoke, they're always one size, it's just the plugin can change FFT size at which point it resets internally. The phase vocoder is being used for time-stretching, and the resultant audio is then interpolated to create a pitch-shifting effect. $\endgroup$ Apr 7, 2022 at 21:26
  • $\begingroup$ Okay, so ultimately, it looks like a pitch shifter. You might want to look at this paper. It has more than the phase vocoder, but it's based on the phase vocoder. I might have some old MATLAB code laying around. But the width of the analysis window should be the same as the width of the synthesis window. But the window hop in the input is shorter than the window hop in the output if you're time-stretching. free copy here $\endgroup$ Apr 8, 2022 at 6:05

1 Answer 1

1
$\begingroup$

I'm working on a spectral processing plugin that's predicated on allowing the user to manipulate per-bin values in an STFT algorithm

This begs the question "why" ? That's not particularly useful and very difficult to implement without creating significant artifacts (due to time domain aliasing). It would help to understand what your specific application and requirements are.

Best way to get the bin amplitudes of an audio DFT "normalized" 0-1

There is no best way that I'm aware of, because that's typically not done. Normalizing each bin individually makes the spectrum white. Audio is decidedly non-white and by normalizing you probably also get a lot of noise amplification in the low-energy bins.

Human perception of audio is typically log/log, i.e. using a logarithmic frequency axis and logarithmic level axis (in dB). The STFT isn't a great fit for that since the bin spacing is linear. The bins at low frequencies tend to be too wide and the bins at high frequencies are way too narrow.

The common approach is to process the date in log frequency bands (3rd octaves for example). This can be done with a weighted STFT or a bandpass filter bank. The STFT needs to be large enough to accommodate the lowest frequency resolution you need. This may cause some non-trivial latency which may or may not be a problem.

Then convert the energy in dB. If your original audio signal is calibrated in real sound pressure (units of Pascal), than you can normalize to dBSPL (using $20 \mu Pa$ as a reference). If not, people typically use dBFS (Full Scale), where $0dB$ is your clipping point.

$\endgroup$
3
  • $\begingroup$ Apologies, I may not have been clear in my explanation. I'm looking to temporarily normalize these bins so they can be processed (i.e., if bin amplitude is less than 0.5, set amplitude to 0). I would then apply the inverse of the same normalization function before feeding the values back through the inverse FFT. $\endgroup$ Apr 7, 2022 at 2:43
  • $\begingroup$ Understood. But you will still end up with massive time aliasing. Also this approach doesn't allow you to distinguish between the "important" and "garbage" bins. $\endgroup$
    – Hilmar
    Apr 7, 2022 at 17:48
  • $\begingroup$ Gotcha. Time aliasing isn't an issue for me at all—weird FFT artifacts are actually kind of the point of the plugin, it's kind of meant to sound like a digital algorithm gone wrong. In terms of distinguishing "important" and "garbage" bins, how would one normally go about that? $\endgroup$ Apr 7, 2022 at 21:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.