iOS FFT绘制光谱

我读过这些问题:

使用Apple FFT和加速框架

使用Accelerate框架进行FFT时如何设置缓冲区?

iOS FFT Accerelate.framework在播放期间绘制频谱

它们都描述了如何使用加速框架设置fft。 在他们的帮助下,我能够设置fft并获得一个基本的频谱分析仪。 现在,我正在显示我从fft获得的所有值。 但是,我只想显示10-15或一个可变数量的条形图来确定某些频率。 就像iTunes或WinAmp Level Meter一样。 1.我是否需要从一系列频率中平均幅度值? 或者他们只是向您展示特定频率条的幅度? 2.此外,我是否需要将我的幅度值转换为db? 3.如何将数据映射到特定范围。 我是否会根据声音bitdepth的最大db范围进行映射? 获取bin的最大值将导致跳转最大映射值。

我的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的值。 绘制一点:

 CGPointMake(i, kMaxSpectrumHeight * (1 - frequencyArray[i]/-96.)); 

给我一个相当圆的曲线:

plot1

如果我没有转换为db,我可以通过将我的数组值乘以10000绘制并得到漂亮的峰值。

plot2

我做错了吗? 如何显示可变数量的条形图?

  1. 我是否需要从一系列频率中平均幅度值? 或者他们只是向您展示特定频率条的幅度?

是的,您肯定需要在您定义的波段上平均值。 只显示一个FFT bin是疯狂的。

  1. 另外,我需要将我的幅度值转换为db吗?

是:dB是对数刻度。 并非巧合的是,人类听觉也可以(大致)以对数尺度工作。 因此,如果在绘制值之前采用值的log2(),则值对于人类来说将更自然。

  1. 如何将数据映射到特定范围。 我是否会根据声音bitdepth的最大db范围进行映射? 获取bin的最大值将导致跳转最大映射值。

我发现最简单的事情(概念上至少)是将你的值从任何格式转换为0..1 ,即’normalized and scaled’浮点值。 然后,从那里你可以转换为你需要绘制的东西。 例如

 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;