【问题标题】:iOS FFT Draw spectrumiOS FFT 绘制频谱
【发布时间】:2013-03-12 04:58:24
【问题描述】:

我已经阅读了这些问题:

Using the Apple FFT and Accelerate Framework

How do I set up a buffer when doing an FFT using the Accelerate framework?

iOS FFT Accerelate.framework draw spectrum during playback

它们都描述了如何使用加速框架设置 fft。在他们的帮助下,我能够设置 fft 并获得基本的频谱分析仪。现在,我正在显示从 fft 获得的所有值。但是,我只想显示 10 到 15 个或可变数量的条形来表示某些频率。就像 iTunes 或 WinAmp 电平表一样。 1. 我是否需要对一系列频率的幅度值进行平均?还是它们只是向您显示特定频率条的幅度? 2. 另外,我需要将幅度值转换为 db 吗? 3. 如何将我的数据映射到某个范围。我是否映射到我的声音位深度的最大分贝范围?获取 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

我做错了什么吗?以及如何显示可变数量的柱?

【问题讨论】:

  • 只是想知道:但你有呈现条形的最终代码吗?我很难从下面的答案中知道该去哪里。

标签: ios core-audio fft spectrum vdsp


【解决方案1】:
  1. 我是否需要对一系列频率的幅度值进行平均?还是它们只是向您显示特定频率条的幅度?

是的,您肯定需要在您定义的范围内取平均值。只显示一个 FFT bin 太疯狂了。

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

是:dB 是对数刻度。并非巧合的是,人类的听觉也(大致)在对数尺度上起作用。因此,如果您在绘制它们之前取值的 log2(),那么这些值对人类来说看起来会更自然。

  1. 如何将我的数据映射到某个范围。我是否映射到我的声音位深度的最大分贝范围?获得 bin 的最大值将 导致最大映射值跳跃。

我发现最容易做的事情(至少在概念上)是将您的值从任何格式转换为0..1,即“标准化和缩放”浮点值。然后,如果需要,您可以从那里转换为您需要绘制的东西。例如

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;

【讨论】:

    猜你喜欢
    • 2012-05-25
    • 1970-01-01
    • 2013-08-11
    • 1970-01-01
    • 2013-05-07
    • 2020-08-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多