Получить iPhone mp3 частоту от AvassetReader и vDSP_FFT - PullRequest
2 голосов
/ 02 мая 2011

Я пытаюсь получить частоту из музыкальной библиотеки iPhone / iPod для приложения спектра в библиотеке iPod, помогая себе чтение-аудио-сэмплов через avassetreader для получения сэмплов, а затем с помощью using-the-apple-fft-and-accelerate-framework и Apple vDSP Samples , но почему-то я где-то ошибаюсь и не могу рассчитать частоту.

Итакшаг за шагом:

  • чтение аудиосэмпла
  • Окно Ханнинга
  • вычисление FFT

Это правильный способ получения частот отбиблиотека iPod mp3?

Вот мой код:

static COMPLEX_SPLIT    A;  
static FFTSetup         setupReal;  
static uint32_t         log2n, n, nOver2;  
static int32_t          stride;  
static float            *obtainedReal;  
static float            scale;  

+ (void)initialize  
{  
    log2n = 10;  
   n = 1 << log2n;  

    stride = 1;  
    nOver2 = n / 2;  
    A.realp = (float *) malloc(nOver2 * sizeof(float));  
    A.imagp = (float *) malloc(nOver2 * sizeof(float));  

    obtainedReal = (float *) malloc(n * sizeof(float));  
    setupReal = vDSP_create_fftsetup(log2n, FFT_RADIX2);  
}  


- (float) performAcceleratedFastFourierTransForAudioBuffer:(AudioBufferList)ioData   
{     
    NSUInteger * sampleIn = (NSUInteger *)ioData.mBuffers[0].mData;
    for (int i = 0; i < nOver2; i++) {
    double multiplier = 0.5 * (1 - cos(2*M_PI*i/nOver2-1));
        A.realp[i] = multiplier * sampleIn[i];
        A.imagp[i] = 0;
    }

    memset(ioData.mBuffers[0].mData, 0, ioData.mBuffers[0].mDataByteSize);  
    vDSP_fft_zrip(setupReal, &A, stride, log2n, FFT_FORWARD);  

    vDSP_zvmags(&A, 1, A.realp, 1, nOver2);           

    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);  

    int peakIndex = 0;  
    for (size_t i=1; i < nOver2-1; ++i) {  
        if ((obtainedReal[i] > obtainedReal[i-1]) && (obtainedReal[i] > obtainedReal[i+1]))         
        {  
            peakIndex = i;  
            break;  
        }  
    }  

    //here I don't know how to calculate frequency with my data   
    float frequency = obtainedReal[peakIndex-1] / 44100 / n;

    vDSP_destroy_fftsetup(setupReal);  
    free(obtainedReal);  
    free(A.realp);  
    free(A.imagp);  

    return frequency;  
}  

Я получил 1.485757 и 1.332233 в качестве моих первых частот

1 Ответ

3 голосов
/ 03 мая 2011

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

Вам нужно либо создать sampleOut, чтобы иметь длину 2*n, и установить любое другое значение (действительные части) или, что еще лучше, вы можете обойти vDSP_ctoz() и напрямую скопировать свои входные данные в A.realp и установите A.imagp в нули. vDSP_ctoz() требуется только при взаимодействии с источником, который создает чередующиеся сложные данные.

Редактировать

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

Вы должны быть в состоянии найти пики, используя A.realp после вызова vDSP_zvmags(&A, 1, A.realp, 1, nOver2);. В этой точке A.realp должен содержать квадрат величины выходного сигнала БПФ, который является скалярным. Если вы собираетесь выполнить масштабирование, это следует сделать до операции mag2, но это может не понадобиться, если вы просто ищете пики.

Чтобы получить реальные частоты, представленные на выходе FFT, используйте следующую формулу:

F = (i * Fs) / N,   i=0,1,...,N/2

где

i - индекс выходного буфера FFT Fs - частота дискретизации звука N это длина БПФ

так что ваш расчет может выглядеть так:

float frequency = (peakIndex * 44100) / n;

Имейте в виду, что vDSP возвращает только первую половину входного спектра для реального ввода, поскольку вторая половина является избыточной. Таким образом, выход FFT представляет частоты от 0 до Fs/2.

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

...