Получить частоту Hz из аудиопотока на iPhone

Каким будет лучший способ получить значение частоты Hz из аудиопотока (музыки) в iOS? Каковы наилучшие и простые рамки, предоставляемые Apple для этого. Спасибо заранее.

Ответ 1

Вопросы, подобные этому, задаются здесь много. (Я ответил на аналогичную здесь), поэтому я написал небольшой учебник с кодом, который можно использовать даже в коммерческих и закрытых исходных приложениях. Это не обязательно лучший способ, но это способ, которым многие люди понимают. Вам придется изменить его, исходя из того, что вы подразумеваете под "средней величиной Гц каждого короткого музыкального сегмента". Вы имеете в виду основной шаг или частотный центроид, например.

Возможно, вы захотите использовать Apple FFT в ракурсе ускорения, как было предложено другим ответом.

Надеюсь, что это поможет.

http://blog.bjornroche.com/2012/07/frequency-detection-using-fft-aka-pitch.html

Ответ 2

Вот некоторый код, который я использую для выполнения FFT в iOS, используя Accelerate Framework, что делает его довольно быстрым.

//keep all internal stuff inside this struct
    typedef struct FFTHelperRef {
        FFTSetup fftSetup; // Accelerate opaque type that contains setup information for a given FFT transform.
        COMPLEX_SPLIT complexA; // Accelerate type for complex number
        Float32 *outFFTData; // Your fft output data
        Float32 *invertedCheckData; // This thing is to verify correctness of output. Compare it with input.
    } FFTHelperRef;

//сначала - инициализируйте FFTHelperRef этой функцией.

FFTHelperRef * FFTHelperCreate(long numberOfSamples) {

    FFTHelperRef *helperRef = (FFTHelperRef*) malloc(sizeof(FFTHelperRef));
    vDSP_Length log2n = log2f(numberOfSamples);    
    helperRef->fftSetup = vDSP_create_fftsetup(log2n, FFT_RADIX2);
    int nOver2 = numberOfSamples/2;
    helperRef->complexA.realp = (Float32*) malloc(nOver2*sizeof(Float32) );
    helperRef->complexA.imagp = (Float32*) malloc(nOver2*sizeof(Float32) );

    helperRef->outFFTData = (Float32 *) malloc(nOver2*sizeof(Float32) );
    memset(helperRef->outFFTData, 0, nOver2*sizeof(Float32) );

    helperRef->invertedCheckData = (Float32*) malloc(numberOfSamples*sizeof(Float32) );

    return  helperRef;
}

//передайте инициализированный FFTHelperRef, данные и размер данных здесь. Возвращайте данные FFT с размером numSamples/2.

Float32 * computeFFT(FFTHelperRef *fftHelperRef, Float32 *timeDomainData, long numSamples) {
    vDSP_Length log2n = log2f(numSamples);
    Float32 mFFTNormFactor = 1.0/(2*numSamples);

    //Convert float array of reals samples to COMPLEX_SPLIT array A
    vDSP_ctoz((COMPLEX*)timeDomainData, 2, &(fftHelperRef->complexA), 1, numSamples/2);

    //Perform FFT using fftSetup and A
    //Results are returned in A
    vDSP_fft_zrip(fftHelperRef->fftSetup, &(fftHelperRef->complexA), 1, log2n, FFT_FORWARD);

    //scale fft 
    vDSP_vsmul(fftHelperRef->complexA.realp, 1, &mFFTNormFactor, fftHelperRef->complexA.realp, 1, numSamples/2);
    vDSP_vsmul(fftHelperRef->complexA.imagp, 1, &mFFTNormFactor, fftHelperRef->complexA.imagp, 1, numSamples/2);

    vDSP_zvmags(&(fftHelperRef->complexA), 1, fftHelperRef->outFFTData, 1, numSamples/2);

    //to check everything =============================
    vDSP_fft_zrip(fftHelperRef->fftSetup, &(fftHelperRef->complexA), 1, log2n, FFT_INVERSE);
    vDSP_ztoc( &(fftHelperRef->complexA), 1, (COMPLEX *) fftHelperRef->invertedCheckData , 2, numSamples/2);
    //=================================================    

    return fftHelperRef->outFFTData;
}

Используйте его следующим образом:

  • Инициализировать его: FFTHelperCreate (TimeDomainDataLenght);

  • Передайте данные во временной области Float32, получите данные о частотной области при возврате: Float32 * fftData = computeFFT (fftHelper, buffer, frameSize);

Теперь у вас есть массив, где индексы = частоты, значения = величина (квадратные величины?). Согласно теорема Найквиста ваша максимально возможная частота в этом массиве составляет половину вашей частоты дискретизации. То есть, если ваша частота дискретизации = 44100, максимальная частота, которую вы можете кодировать, равна 22050 Гц.

Итак, найдите, что частота Nyquist max для вашей частоты дискретизации: const Float32 NyquistMaxFreq = SAMPLE_RATE/2.0;

Найти Hz легко: Float32 hz = ((Float32) someIndex/(Float32) fftDataSize) * NyquistMaxFreq; (fftDataSize = frameSize/2.0)

Это работает для меня. Если я генерирую определенную частоту в Audacity и воспроизвожу ее - этот код обнаруживает правильный (самый сильный, вам также нужно найти max в fftData для этого).

(до сих пор небольшое несоответствие примерно на 1-2%). Не знаю, почему это происходит. Если кто-то может объяснить мне, почему - это было бы очень полезно.)

EDIT:

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

EDIT: Вот пример проекта: https://github.com/krafter/DetectingAudioFrequency

Ответ 3

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

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