AudioSpectrum::AudioSpectrum(QWidget *parent) :
AbstractAudioScopeWidget(false, parent),
- m_fftCfgs(),
- m_windowFunctions(),
+ m_fftTools(),
m_freqMax(10000),
m_customFreq(false),
m_rescaleMinDist(8),
bool b = true;
b &= connect(m_aResetHz, SIGNAL(triggered()), this, SLOT(slotResetMaxFreq()));
+ b &= connect(ui->windowFunction, SIGNAL(currentIndexChanged(int)), this, SLOT(forceUpdate()));
Q_ASSERT(b);
{
writeConfig();
- QHash<QString, kiss_fftr_cfg>::iterator i;
- for (i = m_fftCfgs.begin(); i != m_fftCfgs.end(); i++) {
- free(*i);
- }
delete m_aResetHz;
}
// 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];
+ // Get the spectral power distribution of the input samples,
+ // using the given window size and function
float freqSpectrum[fftWindow/2];
-
- // Prepare frequency space vector. The resulting FFT vector is only half as long.
- kiss_fft_cpx freqData[fftWindow/2];
-
-
-
- // Copy the first channel's audio into a vector for the FFT display
- // (only one channel handled at the moment)
- if (num_samples < fftWindow) {
- std::fill(&data[num_samples], &data[fftWindow-1], 0);
- }
-
FFTTools::WindowType windowType = (FFTTools::WindowType) ui->windowFunction->itemData(ui->windowFunction->currentIndex()).toInt();
- QVector<float> window;
- float windowScaleFactor = 1;
- if (windowType != FFTTools::Window_Rect) {
- const QString signature = FFTTools::windowSignature(windowType, fftWindow, 0);
- if (m_windowFunctions.contains(signature)) {
-#ifdef DEBUG_AUDIOSPEC
- qDebug() << "Re-using window function with signature " << signature;
-#endif
- window = m_windowFunctions.value(signature);
- } else {
-#ifdef DEBUG_AUDIOSPEC
- qDebug() << "Building new window function with signature " << signature;
-#endif
- window = FFTTools::window(windowType, fftWindow, 0);
- m_windowFunctions.insert(signature, window);
- }
- windowScaleFactor = 1.0/window[fftWindow];
- }
-
- // Normalize signals to [0,1] to get correct dB values later on
- for (int i = 0; i < num_samples && i < fftWindow; i++) {
- if (windowType != FFTTools::Window_Rect) {
- data[i] = (float) audioFrame.data()[i*num_channels] / 32767.0f * window[i];
- } else {
- data[i] = (float) audioFrame.data()[i*num_channels] / 32767.0f;
- }
- }
-
- // Calculate the Fast Fourier Transform for the input data
- kiss_fftr(myCfg, data, freqData);
-
-
- // 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 < fftWindow/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)fftWindow/2.0f))/log(10);;
- }
-
+ m_fftTools.fftNormalized(audioFrame, 0, num_channels, freqSpectrum, windowType, fftWindow, 0);
// Draw the spectrum
hud.fill(qRgba(0,0,0,0));
QPainter davinci(&hud);
- davinci.setPen(AbstractAudioScopeWidget::penLight);
+ davinci.setPen(AbstractScopeWidget::penLight);
int y;
for (int db = -dbDiff; db > m_dBmin; db -= dbDiff) {
davinci.drawText(leftDist + m_innerScopeRect.width() + textDistX, topDist+m_innerScopeRect.height()+6, i18n("%1 dB", m_dBmin));
const uint hzDiff = ceil( ((float)minDistX)/m_innerScopeRect.width() * m_freqMax / 1000 ) * 1000;
- int x;
+ int x = 0;
+ const int rightBorder = leftDist + m_innerScopeRect.width()-1;
y = topDist + m_innerScopeRect.height() + textDistY;
- for (uint hz = 0; hz <= m_freqMax; hz += hzDiff) {
+ for (uint hz = 0; x <= rightBorder; hz += hzDiff) {
+ davinci.setPen(AbstractScopeWidget::penLight);
x = leftDist + m_innerScopeRect.width() * ((float)hz)/m_freqMax;
- davinci.drawLine(x, topDist, x, topDist + m_innerScopeRect.height()+6);
- if (hz < m_freqMax) {
+
+ if (x <= rightBorder) {
+ davinci.drawLine(x, topDist, x, topDist + m_innerScopeRect.height()+6);
+ }
+ if (hz < m_freqMax && x+textDistY < leftDist + m_innerScopeRect.width()) {
davinci.drawText(x-4, y, QVariant(hz/1000).toString());
} else {
- davinci.drawText(x-10, y, i18n("%1 kHz",hz/1000));
+ x = leftDist + m_innerScopeRect.width();
+ davinci.drawLine(x, topDist, x, topDist + m_innerScopeRect.height()+6);
+ davinci.drawText(x-10, y, i18n("%1 kHz").arg((double)m_freqMax/1000, 0, 'f', 1));
}
if (hz > 0) {
- for (uint dHz = 1; dHz < 4; dHz++) {
+ // Draw finer lines between the main lines
+ davinci.setPen(AbstractScopeWidget::penLightDots);
+ for (uint dHz = 3; dHz > 0; dHz--) {
x = leftDist + m_innerScopeRect.width() * ((float)hz - dHz * hzDiff/4.0f)/m_freqMax;
+ if (x > rightBorder) {
+ break;
+ }
davinci.drawLine(x, topDist, x, topDist + m_innerScopeRect.height()-1);
}
}