]> git.sesse.net Git - kdenlive/commitdiff
Audio Spectrum:
authorSimon A. Eugster <simon.eu@gmail.com>
Mon, 6 Dec 2010 16:57:05 +0000 (16:57 +0000)
committerSimon A. Eugster <simon.eu@gmail.com>
Mon, 6 Dec 2010 16:57:05 +0000 (16:57 +0000)
* kiss_fft configuration is cached as well for better performance
* Actual window size is shown in the widget (may differ from the selected window size due to limiting sampling rate)
* Showing some ToolTips, removed Realtime option
* Comments added

svn path=/trunk/kdenlive/; revision=5141

src/audioscopes/audiospectrum.cpp
src/audioscopes/audiospectrum.h
src/widgets/audiospectrum_ui.ui

index 2475094cb42facb808d018f9d767a3a58b76244d..c9986f9dc6ff238acd9177c3c188c5a7173c3b09 100644 (file)
@@ -36,6 +36,7 @@ const QString AudioSpectrum::directions[] =  {"North", "Northeast", "East", "Sou
 
 AudioSpectrum::AudioSpectrum(QWidget *parent) :
         AbstractAudioScopeWidget(false, parent),
+        m_fftCfgs(),
         m_windowFunctions(),
         m_freqMax(10000),
         m_customFreq(false),
@@ -54,6 +55,7 @@ AudioSpectrum::AudioSpectrum(QWidget *parent) :
 
     m_menu->addSeparator();
     m_menu->addAction(m_aResetHz);
+    m_menu->removeAction(m_aRealtime);
 
 
     ui->windowSize->addItem("256", QVariant(256));
@@ -66,21 +68,25 @@ AudioSpectrum::AudioSpectrum(QWidget *parent) :
     ui->windowFunction->addItem(i18n("Hamming window"), FFTTools::Window_Hamming);
 
 
-    m_cfg = kiss_fftr_alloc(ui->windowSize->itemData(ui->windowSize->currentIndex()).toInt(), 0,0,0);
-
-
     bool b = true;
-    b &= connect(ui->windowSize, SIGNAL(currentIndexChanged(int)), this, SLOT(slotUpdateCfg()));
     b &= connect(m_aResetHz, SIGNAL(triggered()), this, SLOT(slotResetMaxFreq()));
     Q_ASSERT(b);
 
+
+    ui->labelFFTSize->setToolTip(i18n("The maximum window size is limited by the number of samples per frame."));
+    ui->windowSize->setToolTip(i18n("A bigger window improves the accuracy at the cost of computational power."));
+    ui->windowFunction->setToolTip(i18n("The rectangular window function is good for signals with equal signal strength (narrow peak), but creates more smearing. See Window function on Wikipedia."));
+
     AbstractScopeWidget::init();
 }
 AudioSpectrum::~AudioSpectrum()
 {
     writeConfig();
 
-    free(m_cfg);
+    QHash<QString, kiss_fftr_cfg>::iterator i;
+    for (i = m_fftCfgs.begin(); i != m_fftCfgs.end(); i++) {
+        free(*i);
+    }
     delete m_aResetHz;
 }
 
@@ -138,31 +144,41 @@ QImage AudioSpectrum::renderAudioScope(uint, const QVector<int16_t> audioFrame,
 
         QTime start = QTime::currentTime();
 
-        bool customCfg = false;
-        kiss_fftr_cfg myCfg = m_cfg;
+
+        // Determine the window size to use. It should be
+        // * not bigger than the number of samples actually available
+        // * divisible by 2
         int fftWindow = ui->windowSize->itemData(ui->windowSize->currentIndex()).toInt();
         if (fftWindow > num_samples) {
             fftWindow = num_samples;
-            customCfg = true;
         }
         if ((fftWindow & 1) == 1) {
             fftWindow--;
-            customCfg = true;
         }
-        if (customCfg) {
+
+        // Show the window size used, for information
+        ui->labelFFTSizeNumber->setText(QVariant(fftWindow).toString());
+
+        // Get the kiss_fft configuration from the config cache
+        // or build a new configuration if the requested one is not available.
+        kiss_fftr_cfg myCfg;
+        const QString signature = cfgSignature(fftWindow);
+        if (m_fftCfgs.contains(signature)) {
+#ifdef DEBUG_AUDIOSPEC
+            qDebug() << "Re-using FFT configuration with size " << fftWindow;
+#endif
+            myCfg = m_fftCfgs.value(signature);
+        } else {
+#ifdef DEBUG_AUDIOSPEC
+            qDebug() << "Creating FFT configuration with size " << fftWindow;
+#endif
             myCfg = kiss_fftr_alloc(fftWindow, 0,0,0);
+            m_fftCfgs.insert(signature, myCfg);
         }
 
         float data[fftWindow];
         float freqSpectrum[fftWindow/2];
 
-        int16_t maxSig = 0;
-        for (int i = 0; i < fftWindow; i++) {
-            if (audioFrame.data()[i*num_channels] > maxSig) {
-                maxSig = audioFrame.data()[i*num_channels];
-            }
-        }
-
         // Prepare frequency space vector. The resulting FFT vector is only half as long.
         kiss_fft_cpx freqData[fftWindow/2];
 
@@ -303,10 +319,6 @@ QImage AudioSpectrum::renderAudioScope(uint, const QVector<int16_t> audioFrame,
         }
 #endif
 
-        if (customCfg) {
-            free(myCfg);
-        }
-
         return spectrum;
     } else {
         emit signalScopeRenderingFinished(0, 1);
@@ -392,13 +404,6 @@ QRect AudioSpectrum::scopeRect() {
     return m_scopeRect;
 }
 
-
-void AudioSpectrum::slotUpdateCfg()
-{
-    free(m_cfg);
-    m_cfg = kiss_fftr_alloc(ui->windowSize->itemData(ui->windowSize->currentIndex()).toInt(), 0,0,0);
-}
-
 void AudioSpectrum::slotResetMaxFreq()
 {
     m_customFreq = false;
@@ -547,6 +552,11 @@ void AudioSpectrum::mouseReleaseEvent(QMouseEvent *event)
     AbstractAudioScopeWidget::mouseReleaseEvent(event);
 }
 
+const QString AudioSpectrum::cfgSignature(const int size)
+{
+    return QString("s%1").arg(size);
+}
+
 
 #ifdef DEBUG_AUDIOSPEC
 #undef DEBUG_AUDIOSPEC
index c3547972fa42caea6b2a504c79decf231d2936a5..45c210073752277490a556a9b347ff8d9c6b78a6 100644 (file)
@@ -8,6 +8,12 @@
  *   (at your option) any later version.                                   *
  ***************************************************************************/
 
+/**
+   Displays a spectral power distribution of audio samples.
+   The frequency distribution is calculated by means of a Fast Fourier Transformation.
+   For more information see Wikipedia:FFT and the code comments.
+*/
+
 #ifndef AUDIOSPECTRUM_H
 #define AUDIOSPECTRUM_H
 
@@ -53,12 +59,12 @@ protected:
 
 private:
     Ui::AudioSpectrum_UI *ui;
-    kiss_fftr_cfg m_cfg;
-    QHash<QString, QVector<float> > m_windowFunctions;
+    QHash<QString, kiss_fftr_cfg> m_fftCfgs; // FFT cfg cache
+    QHash<QString, QVector<float> > m_windowFunctions; // Window function cache
 
     QAction *m_aResetHz;
 
-    // Contains the plot only; m_scopeRect contains text and widgets as well
+    /** Contains the plot only; m_scopeRect contains text and widgets as well */
     QRect m_innerScopeRect;
 
     /** Lower bound for the dB value to display */
@@ -66,13 +72,18 @@ private:
     /** Upper bound (max: 0) */
     int m_dBmax;
 
-    /** Maximum frequency (depends on the sampling rate)
-        Stored for the HUD painter */
+    /** Maximum frequency (limited by the sampling rate if determined automatically).
+        Stored for the painters. */
     uint m_freqMax;
     /** The user has chosen a custom frequency. */
     bool m_customFreq;
 
 
+    /** Returns a signature for a kiss_fft configuration
+        used as a hash in the cache */
+    static const QString cfgSignature(const int size);
+
+
     ///// Movement detection /////
     const int m_rescaleMinDist;
     const float m_rescaleVerticalThreshold;
@@ -88,7 +99,6 @@ private:
 
 
 private slots:
-    void slotUpdateCfg();
     void slotResetMaxFreq();
 
 };
index 39dcb349836811fd5d989437bc2fb420523fd2ab..27f8bab77d975897e46e09dc6977ff2ea37b925a 100644 (file)
    <string>Form</string>
   </property>
   <layout class="QGridLayout" name="gridLayout_2">
-   <item row="1" column="2">
+   <item row="1" column="5">
     <widget class="QComboBox" name="windowSize"/>
    </item>
-   <item row="1" column="0">
+   <item row="1" column="3">
     <spacer name="horizontalSpacer">
      <property name="orientation">
       <enum>Qt::Horizontal</enum>
@@ -30,7 +30,7 @@
      </property>
     </spacer>
    </item>
-   <item row="2" column="2">
+   <item row="2" column="5">
     <spacer name="verticalSpacer">
      <property name="orientation">
       <enum>Qt::Vertical</enum>
      </property>
     </spacer>
    </item>
-   <item row="1" column="1">
+   <item row="1" column="4">
     <widget class="QComboBox" name="windowFunction"/>
    </item>
+   <item row="1" column="1">
+    <widget class="QLabel" name="labelFFTSize">
+     <property name="locale">
+      <locale language="English" country="UnitedStates"/>
+     </property>
+     <property name="text">
+      <string>True FFT size: </string>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="0">
+    <spacer name="horizontalSpacer_2">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="sizeType">
+      <enum>QSizePolicy::Fixed</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>10</width>
+       <height>20</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+   <item row="1" column="2">
+    <widget class="QLabel" name="labelFFTSizeNumber">
+     <property name="text">
+      <string/>
+     </property>
+    </widget>
+   </item>
   </layout>
  </widget>
  <resources/>