1 /***************************************************************************
2 * Copyright (C) 2010 by Simon Andreas Eugster (simon.eu@gmail.com) *
3 * This file is part of kdenlive. See www.kdenlive.org. *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 ***************************************************************************/
18 #define DEBUG_FFTTOOLS
24 FFTTools::FFTTools() :
31 QHash<QString, kiss_fftr_cfg>::iterator i;
32 for (i = m_fftCfgs.begin(); i != m_fftCfgs.end(); i++) {
37 const QString FFTTools::windowSignature(const WindowType windowType, const int size, const float param)
39 return QString("s%1_t%2_p%3").arg(size).arg(windowType).arg(param, 0, 'f', 3);
41 const QString FFTTools::cfgSignature(const int size)
43 return QString("s%1").arg(size);
46 // http://cplusplus.syntaxerrors.info/index.php?title=Cannot_declare_member_function_%E2%80%98static_int_Foo::bar%28%29%E2%80%99_to_have_static_linkage
47 const QVector<float> FFTTools::window(const WindowType windowType, const int size, const float param)
49 // Deliberately avoid converting size to a float
50 // to keep mid an integer.
51 float mid = (size-1)/2;
53 QVector<float> window;
57 return QVector<float>(size+1, 1);
60 window = QVector<float>(size+1);
62 for (int x = 0; x < mid; x++) {
63 window[x] = x/mid + (mid-x)/mid*param;
65 for (int x = mid; x < size; x++) {
66 window[x] = (x-mid)/(max-mid) * param + (max-x)/(max-mid);
68 window[size] = .5 + param/2;
71 qDebug() << "Triangle window (factor " << window[size] << "):";
72 for (int i = 0; i < size; i++) {
73 qDebug() << window[i];
75 qDebug() << "Triangle window end.";
81 // Use a quick version of the Hamming window here: Instead of
82 // interpolating values between (-max/2) and (max/2)
83 // we use integer values instead, ranging from -mid to (max-mid).
84 window = QVector<float>(size+1);
86 for (int x = 0; x < size; x++) {
87 window[x] = .54 + .46 * cos( 2*M_PI*(x-mid) / size );
90 // Integrating the cosine over the window function results in
91 // an area of 0; So only the constant factor 0.54 counts.
95 qDebug() << "Hanning window (factor " << window[size] << "):";
96 for (int i = 0; i < size; i++) {
97 qDebug() << window[i];
99 qDebug() << "Hanning window end.";
106 return QVector<float>();
109 void FFTTools::fftNormalized(const QVector<int16_t> audioFrame, const uint channel, const uint numChannels, float *freqSpectrum,
110 const WindowType windowType, const uint windowSize, const float param)
112 #ifdef DEBUG_FFTTOOLS
113 QTime start = QTime::currentTime();
116 const uint numSamples = audioFrame.size()/numChannels;
118 Q_ASSERT((windowSize & 1) == 0);
120 const QString cfgSig = cfgSignature(windowSize);
121 const QString winSig = windowSignature(windowType, windowSize, param);
124 // Get the kiss_fft configuration from the config cache
125 // or build a new configuration if the requested one is not available.
127 if (m_fftCfgs.contains(cfgSig)) {
128 #ifdef DEBUG_FFTTOOLS
129 qDebug() << "Re-using FFT configuration with size " << windowSize;
131 myCfg = m_fftCfgs.value(cfgSig);
133 #ifdef DEBUG_FFTTOOLS
134 qDebug() << "Creating FFT configuration with size " << windowSize;
136 myCfg = kiss_fftr_alloc(windowSize, 0,0,0);
137 m_fftCfgs.insert(cfgSig, myCfg);
140 // Get the window function from the cache
141 // (except for a rectangular window; nothing to to there.
142 QVector<float> window;
143 float windowScaleFactor = 1;
144 if (windowType != FFTTools::Window_Rect) {
146 if (m_windowFunctions.contains(winSig)) {
147 #ifdef DEBUG_FFTTOOLS
148 qDebug() << "Re-using window function with signature " << winSig;
150 window = m_windowFunctions.value(winSig);
152 #ifdef DEBUG_FFTTOOLS
153 qDebug() << "Building new window function with signature " << winSig;
155 window = FFTTools::window(windowType, windowSize, 0);
156 m_windowFunctions.insert(winSig, window);
158 windowScaleFactor = 1.0/window[windowSize];
162 // Prepare frequency space vector. The resulting FFT vector is only half as long.
163 kiss_fft_cpx freqData[windowSize/2];
164 float data[windowSize];
166 // Copy the first channel's audio into a vector for the FFT display;
167 // Fill the data vector indices that cannot be covered with sample data with 0
168 if (numSamples < windowSize) {
169 std::fill(&data[numSamples], &data[windowSize-1], 0);
171 // Normalize signals to [0,1] to get correct dB values later on
172 for (int i = 0; i < numSamples && i < windowSize; i++) {
173 // Performance note: Benchmarking has shown that using the if/else inside the loop
174 // does not do noticeable worse than keeping it outside (perhaps the branch predictor
175 // is good enough), so it remains in there for better readability.
176 if (windowType != FFTTools::Window_Rect) {
177 data[i] = (float) audioFrame.data()[i*numChannels] / 32767.0f * window[i];
179 data[i] = (float) audioFrame.data()[i*numChannels] / 32767.0f;
183 // Calculate the Fast Fourier Transform for the input data
184 kiss_fftr(myCfg, data, freqData);
187 // Logarithmic scale: 20 * log ( 2 * magnitude / N ) with magnitude = sqrt(r² + i²)
188 // with N = FFT size (after FFT, 1/2 window size)
189 for (int i = 0; i < windowSize/2; i++) {
190 // Logarithmic scale: 20 * log ( 2 * magnitude / N ) with magnitude = sqrt(r² + i²)
191 // with N = FFT size (after FFT, 1/2 window size)
192 freqSpectrum[i] = 20*log(pow(pow(fabs(freqData[i].r * windowScaleFactor),2) + pow(fabs(freqData[i].i * windowScaleFactor),2), .5)/((float)windowSize/2.0f))/log(10);;
195 #ifdef DEBUG_FFTTOOLS
196 qDebug() << "Calculated FFT in " << start.elapsed() << " ms.";
200 #ifdef DEBUG_FFTTOOLS
201 #undef DEBUG_FFTTOOLS