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 // Uncomment for debugging
19 //#define DEBUG_FFTTOOLS
27 FFTTools::FFTTools() :
34 QHash<QString, kiss_fftr_cfg>::iterator i;
35 for (i = m_fftCfgs.begin(); i != m_fftCfgs.end(); i++) {
40 const QString FFTTools::windowSignature(const WindowType windowType, const int size, const float param)
42 return QString("s%1_t%2_p%3").arg(size).arg(windowType).arg(param, 0, 'f', 3);
44 const QString FFTTools::cfgSignature(const int size)
46 return QString("s%1").arg(size);
49 // 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
50 const QVector<float> FFTTools::window(const WindowType windowType, const int size, const float param)
54 // Deliberately avoid converting size to a float
55 // to keep mid an integer.
56 float mid = (size-1)/2;
58 QVector<float> window;
62 return QVector<float>(size+1, 1);
65 window = QVector<float>(size+1);
67 for (int x = 0; x < mid; x++) {
68 window[x] = x/mid + (mid-x)/mid*param;
70 for (int x = mid; x < size; x++) {
71 window[x] = (x-mid)/(max-mid) * param + (max-x)/(max-mid);
73 window[size] = .5 + param/2;
76 qDebug() << "Triangle window (factor " << window[size] << "):";
77 for (int i = 0; i < size; i++) {
78 qDebug() << window[i];
80 qDebug() << "Triangle window end.";
86 // Use a quick version of the Hamming window here: Instead of
87 // interpolating values between (-max/2) and (max/2)
88 // we use integer values instead, ranging from -mid to (max-mid).
89 window = QVector<float>(size+1);
91 for (int x = 0; x < size; x++) {
92 window[x] = .54 + .46 * cos( 2*M_PI*(x-mid) / size );
95 // Integrating the cosine over the window function results in
96 // an area of 0; So only the constant factor 0.54 counts.
100 qDebug() << "Hanning window (factor " << window[size] << "):";
101 for (int i = 0; i < size; i++) {
102 qDebug() << window[i];
104 qDebug() << "Hanning window end.";
111 return QVector<float>();
114 void FFTTools::fftNormalized(const QVector<int16_t> audioFrame, const uint channel, const uint numChannels, float *freqSpectrum,
115 const WindowType windowType, const uint windowSize, const float param)
117 #ifdef DEBUG_FFTTOOLS
118 QTime start = QTime::currentTime();
121 const uint numSamples = audioFrame.size()/numChannels;
123 Q_ASSERT((windowSize & 1) == 0);
124 Q_ASSERT(windowSize > 0);
126 const QString cfgSig = cfgSignature(windowSize);
127 const QString winSig = windowSignature(windowType, windowSize, param);
130 // Get the kiss_fft configuration from the config cache
131 // or build a new configuration if the requested one is not available.
133 if (m_fftCfgs.contains(cfgSig)) {
134 #ifdef DEBUG_FFTTOOLS
135 qDebug() << "Re-using FFT configuration with size " << windowSize;
137 myCfg = m_fftCfgs.value(cfgSig);
139 #ifdef DEBUG_FFTTOOLS
140 qDebug() << "Creating FFT configuration with size " << windowSize;
142 myCfg = kiss_fftr_alloc(windowSize, 0,0,0);
143 m_fftCfgs.insert(cfgSig, myCfg);
146 // Get the window function from the cache
147 // (except for a rectangular window; nothing to to there.
148 QVector<float> window;
149 float windowScaleFactor = 1;
150 if (windowType != FFTTools::Window_Rect) {
152 if (m_windowFunctions.contains(winSig)) {
153 #ifdef DEBUG_FFTTOOLS
154 qDebug() << "Re-using window function with signature " << winSig;
156 window = m_windowFunctions.value(winSig);
158 #ifdef DEBUG_FFTTOOLS
159 qDebug() << "Building new window function with signature " << winSig;
161 window = FFTTools::window(windowType, windowSize, 0);
162 m_windowFunctions.insert(winSig, window);
164 windowScaleFactor = 1.0/window[windowSize];
168 // Prepare frequency space vector. The resulting FFT vector is only half as long.
169 kiss_fft_cpx freqData[windowSize/2];
170 float data[windowSize];
172 // Copy the first channel's audio into a vector for the FFT display;
173 // Fill the data vector indices that cannot be covered with sample data with 0
174 if (numSamples < windowSize) {
175 std::fill(&data[numSamples], &data[windowSize-1], 0);
177 // Normalize signals to [0,1] to get correct dB values later on
178 for (uint i = 0; i < numSamples && i < windowSize; i++) {
179 // Performance note: Benchmarking has shown that using the if/else inside the loop
180 // does not do noticeable worse than keeping it outside (perhaps the branch predictor
181 // is good enough), so it remains in there for better readability.
182 if (windowType != FFTTools::Window_Rect) {
183 data[i] = (float) audioFrame.data()[i*numChannels + channel] / 32767.0f * window[i];
185 data[i] = (float) audioFrame.data()[i*numChannels + channel] / 32767.0f;
189 // Calculate the Fast Fourier Transform for the input data
190 kiss_fftr(myCfg, data, freqData);
193 // Logarithmic scale: 20 * log ( 2 * magnitude / N ) with magnitude = sqrt(r² + i²)
194 // with N = FFT size (after FFT, 1/2 window size)
195 for (uint i = 0; i < windowSize/2; i++) {
196 // Logarithmic scale: 20 * log ( 2 * magnitude / N ) with magnitude = sqrt(r² + i²)
197 // with N = FFT size (after FFT, 1/2 window size)
198 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);;
202 #ifdef DEBUG_FFTTOOLS
204 mFile.open("/tmp/freq.m");
206 qDebug() << "Opening file failed.";
210 for (int sample = 0; sample < 256; sample++) {
211 mFile << data[sample] << " ";
215 mFile << "freq = [ ";
216 for (int sample = 0; sample < 256; sample++) {
217 mFile << freqData[sample].r << "+" << freqData[sample].i << "*i ";
222 qDebug() << "File written.";
226 #ifdef DEBUG_FFTTOOLS
227 qDebug() << "Calculated FFT in " << start.elapsed() << " ms.";
231 #ifdef DEBUG_FFTTOOLS
232 #undef DEBUG_FFTTOOLS