]> git.sesse.net Git - kdenlive/blobdiff - src/audioscopes/ffttools.cpp
Fix a couple of compile warnings because of unused and uninitialized variables.
[kdenlive] / src / audioscopes / ffttools.cpp
index 53d02f356824e5e5a0f2a4260e3fae047939b726..3d1ae83fb624dba7639e56deca026735dbedffc7 100644 (file)
 
 #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() :
@@ -172,7 +175,7 @@ void FFTTools::fftNormalized(const QVector<int16_t> audioFrame, const uint chann
         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.
@@ -189,17 +192,145 @@ void FFTTools::fftNormalized(const QVector<int16_t> audioFrame, const uint chann
 
     // 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