Как получить iOS Accelerate FFT результаты в частотных величинах?
Я использую этот код (на основе образца Apple AudioRouch):
void FFTHelper::ComputeFFT(Float32* inAudioData, Float32* outFFTData)
{
if (inAudioData == NULL || outFFTData == NULL) return;
// Generate a split complex vector from the real data
vDSP_ctoz((COMPLEX *)inAudioData, 2, &mDspSplitComplex, 1, mFFTLength);
// Take the fft and scale appropriately
vDSP_fft_zrip(mSpectrumAnalysis, &mDspSplitComplex, 1, mLog2N, kFFTDirection_Forward);
vDSP_vsmul(mDspSplitComplex.realp, 1, &mFFTNormFactor, mDspSplitComplex.realp, 1, mFFTLength);
vDSP_vsmul(mDspSplitComplex.imagp, 1, &mFFTNormFactor, mDspSplitComplex.imagp, 1, mFFTLength);
// Zero out the nyquist value
mDspSplitComplex.imagp[0] = 0.0;
// Complex vector magnitudes squared; single precision.
// Calculates the squared magnitudes of complex vector A.
vDSP_zvmags(&mDspSplitComplex, 1, outFFTData, 1, mFFTLength);
}
Чтобы вычислить БПФ на простейшем из возможных - синусоидальный сигнал 1 Гц (смещенный вверх на 1 единицу):
Float32 waveFreq = 1.0;
int samplesCount = 1024;
Float32 samplesPerSecond = 1000; //sample rate
Float32 dt = 1 / samplesPerSecond;
Float32 sd = M_PI * 2.0 * waveFreq;
FFTHelper *mFFTHelper = new FFTHelper(samplesCount);
Float32 NyquistMaxFreq = samplesPerSecond/2.0;
Float32 fftDataSize = samplesCount/2.0;
Float32 *sinusoidOriginal = (Float32 *)malloc(sizeof(Float32) * samplesCount);
Float32 *outFFTData = (Float32 *)malloc(sizeof(Float32) * fftDataSize);
// 2. Generate sin samples:
for (int i = 0; i < samplesCount; i++) {
Float32 x = dt * i;
sinusoidOriginal[i] = sin(sd * x) + 1;
[originalPlot addVector2D:GLVector2DMake(x, sinusoidOriginal[i])];
}
mFFTHelper->ComputeFFT(sinusoidOriginal, outFFTData);
for (int i = 0; i < fftDataSize; i++) {
Float32 hz = ((Float32)i / (Float32)fftDataSize) * NyquistMaxFreq;
GLfloat mag = outFFTData[i];
[fftPlot addVector2D:GLVector2DMake(hz, 0)];
[fftPlot addVector2D:GLVector2DMake(hz, mag)];
}
Результат, который я получаю:
Черные линии - результаты плоттера от FTT, расположенные горизонтально на их частотах. Значение DC (1-я черная линия слева) выглядит хорошо, правильно представляет y = sin(x) + 1 вертикальное смещение.
Но почему 2-ая черная линия, которая представляет единственную частоту, присутствующую в уравнении синуса, не имеет величины = 1 и точно не остается на частоте 1 Гц?
Кто-нибудь может указать мне на функцию vDSP для преобразования результатов FFT в единицы измерения из входного сигнала?
1 ответ
Короткий ответ:
Вы, кажется, используете код из моего ответа из этого места: /questions/17430068/poluchit-chastotu-gts-iz-audiopotoka-na-iphone/17430091#17430091 И это здорово, но:
Вы не можете избежать этих дополнительных частот в вашем сигнале.
Генерируемый вами сигнал (синусоида) - это бесконечный сигнал. И если вы "обрезаете его" и "используете только часть сигнала", это создает "шум отсечения" на обоих концах.
Но вы можете минимизировать шум, беря большие входные порции и используя оконное преобразование перед FFT. Accelerate Framework предоставляет несколько хороших и простых оконных функций. ( Например, функция Ханна, vDSP_hann_window) Также - используйте более крупные входные блоки. Чем больше вход, тем точнее определение частоты.
Смотрите эту статью, Google: Spectral Leakage, оконная функция.