Использование платформы Apple FFT и Accelerate



кто-нибудь использовал Apple FFT для приложения iPhone еще или знаете, где я могу найти пример приложения о том, как его использовать? Я знаю, что у Apple есть некоторый образец кода, но я не совсем уверен, как реализовать его в реальном проекте.

633   4  

4 ответов:

Я только что получил код БПФ, работающий для проекта iPhone:

  • создать новый проект
  • удалить все файлы, кроме главного.м и xxx_info.файл plist
  • переход к настройкам проекта и поиск pch и остановить его от попытки загрузить .pch (видя, как мы только что удалили его)
  • скопируйте вставить пример кода над тем, что у вас есть в main.м
  • удалите строку, которая #включает углерод. Углерод для OSX.
  • удалите все фреймворки и добавьте accelerate framework

вам также может потребоваться удалить запись из info.plist, который говорит проекту загрузить xib, но я на 90% уверен, что вам не нужно беспокоиться об этом.

Примечание: выходы программы на консоль, результаты выходят как 0.000 это не ошибка - это просто очень очень быстро

этот код действительно тупо неясен; он щедро комментируется, но комментарии не делают на самом деле сделать жизнь легче.

в основном в основе его лежит:

vDSP_fft_zrip(setupReal, &A, stride, log2n, FFT_FORWARD);
vDSP_fft_zrip(setupReal, &A, stride, log2n, FFT_INVERSE);

FFT на N реальных поплавках, а затем обратный, чтобы вернуться туда, где мы начали. ИС стоит на месте, а значит и получает перезаписаны Это причина для всей этой специальной упаковки malarkey-так что мы можем раздавить возвращаемое значение в том же пространстве, что и значение отправки.

чтобы дать некоторую перспективу (например, как в: Почему мы должны использовать эту функцию в первую очередь?), Предположим, мы хотим выполнить обнаружение высоты тона на микрофонном входе, и мы настроили его так, чтобы некоторый обратный вызов срабатывал каждый раз, когда микрофон попадает в 1024 поплавка. Предположим, частота дискретизации микрофона была 44,1 кГц, так что это ~44 кадров/сек.

Итак, наше временное окно независимо от продолжительности времени 1024 образцов, т. е. 1/44 С.

таким образом, мы бы упаковать с 1024 поплавков от микрофона, установить log2n=10 (2^10=1024), предварительно вычислить некоторые катушки (setupReal) и:

vDSP_fft_zrip(setupReal, &A, stride, log2n, FFT_FORWARD);

теперь A будет содержать N / 2 комплексных чисел. Они представляют собой N / 2 частотных бункера:

  • bin[1].idealFreq = 44Hz -- т. е. самая низкая частота, которую мы можем надежно обнаружить, - это одна полная волна в этом окне, т. е. волна 44Hz.

  • bin[2].idealFreq = 2 * 44Hz

  • etc.

  • bin[512].idealFreq = 512 * 44Hz -- самая высокая частота, которую мы можем обнаружить (известно как частота Найквиста), где каждая пара точек представляет собой волну, т. е. 512 полных волн в окне, т. е. 512 * 44Hz, или: n/2 * bin[1].idealFreq

  • на самом деле есть дополнительный бункер, Bin[0], который часто называют "смещение постоянного тока". Так получилось, что Bin[0] и Bin[n/2] всегда будут иметь сложный компонент 0, поэтому A[0].realp используется для хранения Bin[0] и A[0].imagp используется для хранения Bin[n / 2]

и величина каждого комплексное число-это количество энергии, вибрирующей вокруг этой частоты.

так что, как вы можете видеть, это не будет очень большой детектор высоты тона, поскольку он не имеет почти достаточно тонкой детализации. Есть хитрый трюк извлечение точных частот из БПФ бункеров с помощью изменения фазы между кадрами чтобы получить точную частоту для данного ящика.

хорошо, теперь на код:

обратите внимание на ' ip 'в vDSP_fft_zrip, =' in place ' ie output перезаписывает A ('r' означает, что он принимает реальные входные данные)

посмотрите документацию на vDSP_fft_zrip,

реальные данные хранятся в комплексе разделения форма, с нечетными реалами, хранящимися на воображаемая сторона расщепленного комплекса форма и даже реалы в сохраненном на реальная сторона.

это, наверное, самое трудное для понимания. Мы используем один и тот же контейнер (&A) на протяжении всего процесса. поэтому в начале мы хотим заполнить его с n вещественными числами. после БПФ он будет содержать N / 2 комплексных чисел. затем мы бросаем это в обратное преобразование и, надеюсь, получим наши исходные N вещественных чисел.

теперь структура его настройки для комплексных значений. Поэтому vDSP нужно стандартизировать, как упаковать в него реальные числа.

Итак, сначала мы генерируем N вещественных чисел: 1, 2, ..., n

for (i = 0; i < n; i++)
    originalReal[i] = (float) (i + 1);

Далее мы упаковываем их в комплекс as n / 2 # s:

// 1. masquerades n real #s as n/2 complex #s = {1+2i, 3+4i, ...}
// 2. splits to 
//   A.realP = {1,3,...} (n/2 elts)
//   A.compP = {2,4,...} (n/2 elts)
//
vDSP_ctoz(
          (COMPLEX *) originalReal, 
          2,                            // stride 2, as each complex # is 2 floats
          &A, 
          1,                            // stride 1 in A.realP & .compP
          nOver2);                      // n/2 elts

вам действительно нужно посмотреть, как A выделяется, чтобы получить это, возможно, посмотрите COMPLEX_SPLIT в документации.

A.realp = (float *) malloc(nOver2 * sizeof(float));
A.imagp = (float *) malloc(nOver2 * sizeof(float));

Далее мы делаем предварительный расчет.


быстрый класс DSP для математических бодов: теория Фурье занимает много времени, чтобы получить вашу голову вокруг (я смотрю на это и выключается в течение нескольких лет)

в cisoid это:

z = exp(i.theta) = cos(theta) + i.sin(theta)

т. е. точка на единичной окружности в комплексной плоскости.

когда вы умножаете комплексные числа, углы складываются. Таким образом, z^k будет продолжать прыгать вокруг единичного круга; z^k можно найти под углом k. theta

  • выберите z1 = 0+1i, т. е. четверть оборота от реальной оси, и обратите внимание, что z1^2 z1^3 z1^4 каждый дает еще четверть оборота, так что z1^4 = 1

  • выберите z2 = -1, т. е. пол-оборота. также z2^4 = 1, но z2 завершил 2 цикла в этой точке (z2^2 также = 1). Таким образом, вы можете думать о z1 как о основной частоте и z2 как о первой гармонике

  • аналогично z3 = точка "три четверти оборота" т. е.-i завершает ровно 3 цикла, но на самом деле движение вперед 3/4 каждый раз совпадает с движением назад 1/4 каждый время

т. е. z3-это просто z1, но в противоположном направлении - это называется aliasing

z2 самая высокая содержательная частота, по мере того как мы выбрали 4 образца для того чтобы держать полную волну.

  • z0 = 1+0i, z0^(что угодно)=1, это смещение постоянного тока

вы можете выразить любой 4-точечный сигнал в виде линейной комбинации z0 z1 и z2 т. е. ты проецируя его на эти базисные векторы

но я слышу, как вы спрашиваете: "Что значит проецировать сигнал на цизоид?"

вы можете думать об этом так: игла вращается вокруг цисоида, поэтому в образце k игла указывает в направлении k.тета, а длина-сигнал[k]. Сигнал, который точно соответствует частоте цисоида, будет выпячивать полученную форму в некотором направлении. Так что если сложить все взносы, вы получите сильный результирующий вектор. Если частота почти совпадает, то выпуклость будет меньше и будет медленно двигаться по кругу. Для сигнала, который не соответствует частоте, вклады будут отменять друг друга.

http://complextoreal.com/tutorials/tutorial-4-fourier-analysis-made-easy-part-1/ поможет вам получить интуитивное понимание.

но суть в том, если мы выбрали для проекта 1024 выборки на {z0,...,z512} мы должны были бы предварительно вычислить z0 через z512, и вот что такое этот шаг предварительного расчета.


обратите внимание, что если вы делаете это в реальном коде, вы, вероятно, хотите сделать это один раз, когда приложение загружается и вызывает дополнительную функцию выпуска один раз, когда он выходит. Не делайте этого много раз-это дорого.

// let's say log2n = 8, so n=2^8=256 samples, or 'harmonics' or 'terms'
// if we pre-calculate the 256th roots of unity (of which there are 256) 
// that will save us time later.
//
// Note that this call creates an array which will need to be released 
// later to avoid leaking
setupReal = vDSP_create_fftsetup(log2n, FFT_RADIX2);

стоит отметить, что если мы установим log2n в eg 8, вы можете бросить эти предварительно рассчитанные значения в любую функцию БПФ, которая использует разрешение

теперь фактические преобразования, используя материал, который мы только что предварительно рассчитали:

vDSP_fft_zrip(setupReal, &A, stride, log2n, FFT_FORWARD);

в этот момент A будет содержать N / 2 комплексных чисел, только первое из них на самом деле является двумя вещественными числами (DC offset, Nyquist#), маскирующимися под комплексное число. Этот обзор документации объясняет эту упаковку. Это довольно аккуратно - в основном это позволяет (сложные) результаты преобразования быть упакованы в тот же объем памяти, что и (реальные, но странно упакованные) входы.

vDSP_fft_zrip(setupReal, &A, stride, log2n, FFT_INVERSE);

и обратно... нам все равно нужно будет распаковать наш исходный массив из A. Затем мы сравниваем только для того, чтобы проверить, что мы вернулись именно с того, с чего начали, выпустили наши предварительно рассчитанные бобины и сделали!

но подождите! перед вами распакуйте, есть одна последняя вещь, которую нужно сделать:

// Need to see the documentation for this one...
// in order to optimise, different routines return values 
// that need to be scaled by different amounts in order to 
// be correct as per the math
// In this case...
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);

вот реальный пример: фрагмент c++, который использует подпрограммы Vdsp fft Accelerate для выполнения автокорреляции на входе аудиоустройства удаленного ввода-вывода. Использование этой структуры довольно сложно, но документация не слишком плохо.

OSStatus DSPCore::initialize (double _sampleRate, uint16_t _bufferSize) {
    sampleRate = _sampleRate;
    bufferSize = _bufferSize;
    peakIndex = 0;
    frequency = 0.f;
    uint32_t maxFrames = getMaxFramesPerSlice();
    displayData = (float*)malloc(maxFrames*sizeof(float));
    bzero(displayData, maxFrames*sizeof(float));
    log2n = log2f(maxFrames);
    n = 1 << log2n;
    assert(n == maxFrames);
    nOver2 = maxFrames/2;
    A.realp = (float*)malloc(nOver2 * sizeof(float));
    A.imagp = (float*)malloc(nOver2 * sizeof(float));
    FFTSetup fftSetup = vDSP_create_fftsetup(log2n, FFT_RADIX2);

    return noErr;
}

void DSPCore::Render(uint32_t numFrames, AudioBufferList *ioData) {

    bufferSize = numFrames;
    float ln = log2f(numFrames);

    //vDSP autocorrelation

    //convert real input to even-odd
    vDSP_ctoz((COMPLEX*)ioData->mBuffers[0].mData, 2, &A, 1, numFrames/2);
    memset(ioData->mBuffers[0].mData, 0, ioData->mBuffers[0].mDataByteSize);
    //fft
    vDSP_fft_zrip(fftSetup, &A, 1, ln, FFT_FORWARD);

    // Absolute square (equivalent to mag^2)
    vDSP_zvmags(&A, 1, A.realp, 1, numFrames/2);
    bzero(A.imagp, (numFrames/2) * sizeof(float));    

    // Inverse FFT
    vDSP_fft_zrip(fftSetup, &A, 1, ln, FFT_INVERSE);

    //convert complex split to real
    vDSP_ztoc(&A, 1, (COMPLEX*)displayData, 2, numFrames/2);

    // Normalize
    float scale = 1.f/displayData[0];
    vDSP_vsmul(displayData, 1, &scale, displayData, 1, numFrames);

    // Naive peak-pick: find the first local maximum
    peakIndex = 0;
    for (size_t ii=1; ii < numFrames-1; ++ii) {
        if ((displayData[ii] > displayData[ii-1]) && (displayData[ii] > displayData[ii+1])) {
            peakIndex = ii;
            break;
        }
    }

    // Calculate frequency
    frequency = sampleRate / peakIndex + quadInterpolate(&displayData[peakIndex-1]);

    bufferSize = numFrames;

    for (int ii=0; ii<ioData->mNumberBuffers; ++ii) {
        bzero(ioData->mBuffers[ii].mData, ioData->mBuffers[ii].mDataByteSize);
    }
}

в то время как я скажу, что БПФ рамки Apple быстро... Вам нужно знать, как работает БПФ, чтобы получить точное определение шага (т. е. вычисление разности фаз на каждом последующем БПФ, чтобы найти точный шаг, а не шаг самого доминирующего бункера).

Я не знаю, поможет ли это, но я загрузил свой объект детектора высоты тона из своего приложения тюнера (musicianskit.com/developer.php). существует пример проекта xCode 4 для загрузки также (Так что вы можете увидеть, как выполнение работ).

Я работаю над загрузкой примера реализации БПФ - так что следите за обновлениями, и я обновлю это, как только это произойдет.

удачи в кодировании!

вот еще один реальный пример: https://github.com/krafter/DetectingAudioFrequency

Comments

    Ничего не найдено.