#include "ffttools.h"
+// Uncomment for debugging, like writing a GNU Octave .m file to /tmp
//#define DEBUG_FFTTOOLS
+
#ifdef DEBUG_FFTTOOLS
#include <QDebug>
#include <QTime>
+#include <fstream>
#endif
FFTTools::FFTTools() :
#ifdef DEBUG_FFTTOOLS
qDebug() << "Creating FFT configuration with size " << windowSize;
#endif
- myCfg = kiss_fftr_alloc(windowSize, 0,0,0);
+ myCfg = kiss_fftr_alloc(windowSize, false,NULL,NULL);
m_fftCfgs.insert(cfgSig, myCfg);
}
std::fill(&data[numSamples], &data[windowSize-1], 0);
}
// Normalize signals to [0,1] to get correct dB values later on
- for (int i = 0; i < numSamples && i < windowSize; i++) {
+ for (uint i = 0; i < numSamples && i < windowSize; i++) {
// Performance note: Benchmarking has shown that using the if/else inside the loop
// does not do noticeable worse than keeping it outside (perhaps the branch predictor
// is good enough), so it remains in there for better readability.
// Logarithmic scale: 20 * log ( 2 * magnitude / N ) with magnitude = sqrt(r² + i²)
// with N = FFT size (after FFT, 1/2 window size)
- for (int i = 0; i < windowSize/2; i++) {
+ for (uint i = 0; i < windowSize/2; i++) {
// Logarithmic scale: 20 * log ( 2 * magnitude / N ) with magnitude = sqrt(r² + i²)
// with N = FFT size (after FFT, 1/2 window size)
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);;
}
+
+#ifdef DEBUG_FFTTOOLS
+ std::ofstream mFile;
+ mFile.open("/tmp/freq.m");
+ if (!mFile) {
+ qDebug() << "Opening file failed.";
+ } else {
+ mFile << "val = [ ";
+
+ for (int sample = 0; sample < 256; sample++) {
+ mFile << data[sample] << " ";
+ }
+ mFile << " ];\n";
+
+ mFile << "freq = [ ";
+ for (int sample = 0; sample < 256; sample++) {
+ mFile << freqData[sample].r << "+" << freqData[sample].i << "*i ";
+ }
+ mFile << " ];\n";
+
+ mFile.close();
+ qDebug() << "File written.";
+ }
+#endif
+
#ifdef DEBUG_FFTTOOLS
qDebug() << "Calculated FFT in " << start.elapsed() << " ms.";
#endif
}
+const QVector<float> FFTTools::interpolatePeakPreserving(const QVector<float> in, const uint targetSize, uint left, uint right, float fill)
+{
+#ifdef DEBUG_FFTTOOLS
+ QTime start = QTime::currentTime();
+#endif
+
+ if (right == 0) {
+ right = in.size()-1;
+ }
+ Q_ASSERT(targetSize > 0);
+ Q_ASSERT(left < right);
+
+ QVector<float> out(targetSize);
+
+
+ float x;
+ float x_prev = 0;
+ uint xi;
+ uint i;
+ if (((float) (right-left))/targetSize < 2) {
+ for (i = 0; i < targetSize; i++) {
+
+ // i: Target index
+ // x: Interpolated source index (float!)
+ // xi: floor(x)
+
+ // Transform [0,targetSize-1] to [left,right]
+ x = ((float) i) / (targetSize-1) * (right-left) + left;
+ xi = (int) floor(x);
+
+ if (x > in.size()-1) {
+ // This may happen if right > in.size()-1; Fill the rest of the vector
+ // with the default value now.
+ break;
+ }
+
+
+ // Use linear interpolation in order to get smoother display
+ if (xi == 0 || xi == (uint) in.size()-1) {
+ // ... except if we are at the left or right border of the input sigal.
+ // Special case here since we consider previous and future values as well for
+ // the actual interpolation (not possible here).
+ out[i] = in[xi];
+ } else {
+ if (in[xi] > in[xi+1]
+ && x_prev < xi) {
+ // This is a hack to preserve peaks.
+ // Consider f = {0, 100, 0}
+ // x = {0.5, 1.5}
+ // Then x is 50 both times, and the 100 peak is lost.
+ // Get it back here for the first x after the peak (which is at xi).
+ // (x is the first after the peak if the previous x was smaller than floor(x).)
+ out[i] = in[xi];
+ } else {
+ out[i] = (xi+1 - x) * in[xi]
+ + (x - xi) * in[xi+1];
+ }
+ }
+ x_prev = x;
+ }
+ } else {
+ // If there are more than 2 samples per pixel in average, then use the maximum of them
+ // since by only looking at the left sample we might miss some maxima.
+ uint src = left;
+// int xi_prev = 0;
+ int points;
+
+#ifdef DEBUG_FFTTOOLS
+ qDebug() << "Interpolation: Ratio over 2; using maximum interpolation";
+#endif
+
+ for (i = 0; i < targetSize; i++) {
+
+ // x: right bound
+ // xi: floor(x)
+ x = ((float) (i+1)) / (targetSize-1) * (right-left) + left;
+ xi = (int) floor(x);
+ points = 0;
+
+ out[i] = fill;
+
+ for (; src < xi && src < (uint) in.size(); src++) {
+ if (out[i] < in[src]) {
+ out[i] = in[src];
+ }
+ points++;
+ }
+
+// xi_prev = xi;
+ }
+ }
+ // Fill the rest of the vector if the right border exceeds the input vector.
+ for (; i < targetSize; i++) {
+ out[i] = fill;
+ }
+
+#ifdef DEBUG_FFTTOOLS
+ qDebug() << "Interpolated " << targetSize << " nodes from " << in.size() << " input points in " << start.elapsed() << " ms";
+#endif
+
+ return out;
+}
+
#ifdef DEBUG_FFTTOOLS
#undef DEBUG_FFTTOOLS
#endif