问题描述
我已经阅读了这些问题:
I've read these question:
它们都描述了如何使用加速框架设置fft。在他们的帮助下,我能够设置fft并获得一个基本的频谱分析仪。现在,我正在显示我从fft获得的所有值。但是,我只想显示10-15或一个可变数量的条形图来确定某些频率。就像iTunes或WinAmp Level Meter一样。
1.我是否需要从一系列频率中平均幅度值?或者他们只是向您展示特定频率条的幅度?
2.另外,我是否需要将我的幅度值转换为db?
3.如何将数据映射到特定范围。我是否会根据声音bitdepth的最大db范围进行映射?获取bin的最大值将导致跳转最大映射值。
They all describe how to setup fft with the accelerate framework. With their help I was able to setup fft and get a basic spectrum analyzer. Right now, I'm displaying all values I got from the fft. However, I only want to show 10-15, or a variable number, of bars respreseting certain frequencies. Just like the iTunes or WinAmp Level Meter.1. Do I need to average magnitude values from a range of frequencies? Or do they just show you a magnitude for the specific frequency bar?2. Also, do I need to convert my magnitude values to db?3. How do I map my data to a certain range. Do I map against the max db range for my sounds bitdepth? Getting the max Value for a bin will lead to jumping max mapping values.
我的RenderCallback:
My RenderCallback:
static OSStatus PlaybackCallback(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData)
{
UInt32 maxSamples = kAudioBufferNumFrames;
UInt32 log2n = log2f(maxSamples); //bins
UInt32 n = 1 << log2n;
UInt32 stride = 1;
UInt32 nOver2 = n/2;
COMPLEX_SPLIT A;
float *originalReal, *obtainedReal, *frequencyArray, *window, *in_real;
in_real = (float *) malloc(maxSamples * sizeof(float));
A.realp = (float *) malloc(nOver2 * sizeof(float));
A.imagp = (float *) malloc(nOver2 * sizeof(float));
memset(A.imagp, 0, nOver2 * sizeof(float));
obtainedReal = (float *) malloc(n * sizeof(float));
originalReal = (float *) malloc(n * sizeof(float));
frequencyArray = (float *) malloc(n * sizeof(float));
//-- window
UInt32 windowSize = maxSamples;
window = (float *) malloc(windowSize * sizeof(float));
memset(window, 0, windowSize * sizeof(float));
// vDSP_hann_window(window, windowSize, vDSP_HANN_DENORM);
vDSP_blkman_window(window, windowSize, 0);
vDSP_vmul(ioBuffer, 1, window, 1, in_real, 1, maxSamples);
//-- window
vDSP_ctoz((COMPLEX*)in_real, 2, &A, 1, maxSamples/2);
vDSP_fft_zrip(fftSetup, &A, stride, log2n, FFT_FORWARD);
vDSP_fft_zrip(fftSetup, &A, stride, log2n, FFT_INVERSE);
float scale = (float) 1.0 / (2 * n);
vDSP_vsmul(A.realp, 1, &scale, A.realp, 1, nOver2);
vDSP_vsmul(A.imagp, 1, &scale, A.imagp, 1, nOver2);
vDSP_ztoc(&A, 1, (COMPLEX *) obtainedReal, 2, nOver2);
vDSP_zvmags(&A, 1, obtainedReal, 1, nOver2);
Float32 one = 1;
vDSP_vdbcon(obtainedReal, 1, &one, obtainedReal, 1, nOver2, 0);
for (int i = 0; i < nOver2; i++) {
frequencyArray[i] = obtainedReal[i];
}
// Extract the maximum value
double fftMax = 0.0;
vDSP_maxmgvD((double *)obtainedReal, 1, &fftMax, nOver2);
float max = sqrt(fftMax);
}
播放一些音乐,我得到的值从-96db到0db。
绘制一个点:
Playing some music, I get values from -96db to 0db.Plotting a point at:
CGPointMake(i, kMaxSpectrumHeight * (1 - frequencyArray[i]/-96.));
给出一条相当圆的曲线:
is giving my a rather rounded curve:
如果我不要转换为db我可以通过将我的数组值乘以10000得到很好的峰值。
If I don't convert to db I can plot by multiplying my array value by 10000 and get nice peaks.
我做错了什么?如何显示可变数量的柱?
Am I doing something totally wrong? And how do I get to showing a variable number of bars?
推荐答案
- 我是否需要从一系列频率中平均幅度值?或者它们只显示特定频率条的幅度?
是的,你肯定需要平均你定义的波段的平均值。只显示一个FFT bin是疯狂的。
Yes, you definitely need to average values across the bands you've defined. Showing just one FFT bin is madness.
- 此外,我是否需要转换我的幅度值到db?
是:dB是对数刻度。并非巧合的是,人类听觉也可以(大致)以对数尺度工作。因此,如果你在绘制它们之前采用值的log2(),那么这些值看起来会更自然。
Yes: dB is a log scale. Not coincidentally, human hearing also works (roughly) on a log scale. The values will therefore look more natural to humans if you take log2() of the values before plotting them.
- 如何将数据映射到特定范围。我是否会根据声音bitdepth的最大db范围进行映射?获取bin的最大值将是
导致跳转最大映射值。
我发现最简单的事情(概念上至少)是将你的值从任何格式转换为 0..1
,即'normalized and scaled'浮点值。然后,从那里你可以转换为你需要绘制的东西。例如
I find the easiest thing to do (conceptually at least) is to convert your values from whatever format into a 0..1
, i.e. 'normalised and scaled' float value. Then from there you can convert if necessary to something you need to plot. For example
SInt16 rawValue = fft[0]; // let's say this comes back as 12990
float scaledValue = rawValue/32767.; // This is MAX_INT for 16-bit;
// dividing we get .396435438 which is much easier for most people
// to see conceptually as 39% of our max possible value
float displayValue = log2(scaledValue);
my_fft[0] = displayValue;
这篇关于iOS FFT绘制光谱的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!