AudioSpectrum::AudioSpectrum(QWidget *parent) :
AbstractAudioScopeWidget(false, parent),
+ m_fftCfgs(),
m_windowFunctions(),
m_freqMax(10000),
m_customFreq(false),
m_menu->addSeparator();
m_menu->addAction(m_aResetHz);
+ m_menu->removeAction(m_aRealtime);
ui->windowSize->addItem("256", QVariant(256));
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;
}
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];
}
#endif
- if (customCfg) {
- free(myCfg);
- }
-
return spectrum;
} else {
emit signalScopeRenderingFinished(0, 1);
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;
AbstractAudioScopeWidget::mouseReleaseEvent(event);
}
+const QString AudioSpectrum::cfgSignature(const int size)
+{
+ return QString("s%1").arg(size);
+}
+
#ifdef DEBUG_AUDIOSPEC
#undef DEBUG_AUDIOSPEC
* (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
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 */
/** 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;
private slots:
- void slotUpdateCfg();
void slotResetMaxFreq();
};
<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>
</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/>