Подтвердить что ты не робот

Спектр рисования iOS FFT

Я прочитал этот вопрос:

Использование Apple FFT и ускорение Framework

Как настроить буфер при выполнении БПФ с использованием рамки Accelerate?

iOS FFT Accerelate.framework рисовать спектр во время воспроизведения

Все они описывают, как настроить fft с ракурсом ускорения. С их помощью я смог настроить fft и получить базовый анализатор спектра. Прямо сейчас, я показываю все значения, которые я получил от fft. Тем не менее, я хочу показать только 10-15 или переменное число баров, пересчитывающих определенные частоты. Точно так же как измеритель уровня iTunes или WinAmp. 1. Нужно ли мне усреднять значения амплитуды из диапазона частот? Или они просто показывают вам величину для конкретной полосы частот? 2. Кроме того, мне нужно преобразовать значения величины в db? 3. Как сопоставить мои данные с определенным диапазоном. Я сопоставляю с диапазоном max db для своих битов бит? Получение максимального значения для бина приведет к максимальному отображению значений перехода.

Мой 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

Я делаю что-то совершенно не так? И как я могу показать переменное количество баров?

4b9b3361

Ответ 1

  • Нужно ли мне усреднять значения амплитуды из диапазона частот? Или они просто показывают вам величину для конкретной полосы частот?

Да, вам определенно нужно усреднять значения по диапазонам, которые вы определили. Показывать только один бит FFT - безумие.

  1. Кроме того, мне нужно преобразовать значения этой величины в db?

Да: дБ - это масштаб журнала. Не случайно человеческий слух также работает (грубо) в масштабе журнала. Поэтому значения будут выглядеть более естественными для людей, если вы берете log2() значений перед их графикой.

  1. Как сопоставить мои данные с определенным диапазоном. Я сопоставляю с диапазоном max db для своих битов бит? Получение максимального значения для бункера будет приводят к максимальному отображению значений перехода.

Я нахожу, что проще всего (по крайней мере концептуально) - преобразовать ваши значения из любого формата в значение 0..1, т.е. нормализованное и масштабируемое значение float. Затем оттуда вы можете конвертировать, если необходимо, что-то, что вам нужно для заговора. Например

SInt16 rawValue = fft[0]; // let 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;