From 64d9e579dd962769d577e14522c5134fc3ebed15 Mon Sep 17 00:00:00 2001 From: "Simon A. Eugster" Date: Wed, 8 Dec 2010 17:50:04 +0000 Subject: [PATCH] Spectrogram: * Minimum and maximum dB can be chosen with vertical dragging * Maximum frequency can be adjusted with horizontal dragging * HUD showing frequency and frame number at mouse position * Only add a new line if there is actually new data available (i.e. not on dB adustments and such) svn path=/trunk/kdenlive/; revision=5153 --- src/audioscopes/audiospectrum.cpp | 8 +- src/audioscopes/audiospectrum.h | 2 +- src/audioscopes/spectrogram.cpp | 379 +++++++++++++++++++++++++----- src/audioscopes/spectrogram.h | 24 +- src/widgets/spectrogram_ui.ui | 30 +-- 5 files changed, 359 insertions(+), 84 deletions(-) diff --git a/src/audioscopes/audiospectrum.cpp b/src/audioscopes/audiospectrum.cpp index bad169f2..71b1cd0d 100644 --- a/src/audioscopes/audiospectrum.cpp +++ b/src/audioscopes/audiospectrum.cpp @@ -64,6 +64,7 @@ AudioSpectrum::AudioSpectrum(QWidget *parent) : Q_ASSERT(b); + // Note: These strings are used in both Spectogram and AudioSpectrum. Ideally change both (if necessary) to reduce workload on translators 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.")); @@ -122,7 +123,8 @@ bool AudioSpectrum::isHUDDependingOnInput() const { return false; } QImage AudioSpectrum::renderBackground(uint) { return QImage(); } -QImage AudioSpectrum::renderAudioScope(uint, const QVector audioFrame, const int freq, const int num_channels, const int num_samples) +QImage AudioSpectrum::renderAudioScope(uint, const QVector audioFrame, const int freq, const int num_channels, + const int num_samples, const int) { if (audioFrame.size() > 63) { if (!m_customFreq) { @@ -350,7 +352,7 @@ void AudioSpectrum::slotResetMaxFreq() void AudioSpectrum::handleMouseDrag(const QPoint movement, const RescaleDirection rescaleDirection, const Qt::KeyboardModifiers rescaleModifiers) { - if (rescaleDirection == AudioSpectrum::North) { + if (rescaleDirection == North) { // Nort-South direction: Adjust the dB scale if ((rescaleModifiers & Qt::ShiftModifier) == 0) { @@ -397,7 +399,7 @@ void AudioSpectrum::handleMouseDrag(const QPoint movement, const RescaleDirectio forceUpdateHUD(); forceUpdateScope(); - } else if (rescaleDirection == AudioSpectrum::East) { + } else if (rescaleDirection == East) { // East-West direction: Adjust the maximum frequency m_freqMax -= 100*movement.x(); if (m_freqMax < MIN_FREQ_VALUE) { diff --git a/src/audioscopes/audiospectrum.h b/src/audioscopes/audiospectrum.h index b159f682..4a3c507d 100644 --- a/src/audioscopes/audiospectrum.h +++ b/src/audioscopes/audiospectrum.h @@ -42,7 +42,7 @@ protected: ///// Implemented methods ///// QRect scopeRect(); QImage renderHUD(uint accelerationFactor); - QImage renderAudioScope(uint accelerationFactor, const QVector audioFrame, const int freq, const int num_channels, const int num_samples); + QImage renderAudioScope(uint accelerationFactor, const QVector audioFrame, const int freq, const int num_channels, const int num_samples, const int newData); QImage renderBackground(uint accelerationFactor); bool isHUDDependingOnInput() const; bool isScopeDependingOnInput() const; diff --git a/src/audioscopes/spectrogram.cpp b/src/audioscopes/spectrogram.cpp index b3eb46af..e84fecdb 100644 --- a/src/audioscopes/spectrogram.cpp +++ b/src/audioscopes/spectrogram.cpp @@ -9,6 +9,7 @@ ***************************************************************************/ #include +#include #include "spectrogram.h" @@ -18,21 +19,34 @@ // Can be less as a pre-rendered image is kept in space. #define SPECTROGRAM_HISTORY_SIZE 1000 +// Uncomment for debugging //#define DEBUG_SPECTROGRAM + #ifdef DEBUG_SPECTROGRAM #include #endif +#define MIN_DB_VALUE -120 +#define MAX_FREQ_VALUE 96000 +#define MIN_FREQ_VALUE 1000 + Spectrogram::Spectrogram(QWidget *parent) : - AbstractAudioScopeWidget(false, parent), + AbstractAudioScopeWidget(true, parent), m_fftTools(), m_fftHistory(), - m_fftHistoryImg() + m_fftHistoryImg(), + m_parameterChanged(false) { ui = new Ui::Spectrogram_UI; ui->setupUi(this); + m_aResetHz = new QAction(i18n("Reset maximum frequency to sampling rate"), this); + + + m_menu->addSeparator(); + m_menu->addAction(m_aResetHz); + m_menu->removeAction(m_aRealtime); ui->windowSize->addItem("256", QVariant(256)); @@ -44,8 +58,15 @@ Spectrogram::Spectrogram(QWidget *parent) : ui->windowFunction->addItem(i18n("Triangular window"), FFTTools::Window_Triangle); ui->windowFunction->addItem(i18n("Hamming window"), FFTTools::Window_Hamming); + // Note: These strings are used in both Spectogram and AudioSpectrum. Ideally change both (if necessary) to reduce workload on translators + 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.")); + bool b = true; + b &= connect(m_aResetHz, SIGNAL(triggered()), this, SLOT(slotResetMaxFreq())); b &= connect(ui->windowFunction, SIGNAL(currentIndexChanged(int)), this, SLOT(forceUpdate())); + b &= connect(this, SIGNAL(signalMousePositionChanged()), this, SLOT(forceUpdateHUD())); Q_ASSERT(b); AbstractScopeWidget::init(); @@ -54,6 +75,8 @@ Spectrogram::Spectrogram(QWidget *parent) : Spectrogram::~Spectrogram() { writeConfig(); + + delete m_aResetHz; } void Spectrogram::readConfig() @@ -108,7 +131,7 @@ QRect Spectrogram::scopeRect() ); m_innerScopeRect = QRect( QPoint( - m_scopeRect.left()+6, // Left + m_scopeRect.left()+56, // Left m_scopeRect.top()+6 // Top ), QPoint( ui->verticalSpacer->geometry().right()-70, @@ -118,13 +141,142 @@ QRect Spectrogram::scopeRect() return m_scopeRect; } -QImage Spectrogram::renderHUD(uint) { return QImage(); } +QImage Spectrogram::renderHUD(uint) +{ + + QTime start = QTime::currentTime(); + + int x, y; + const uint minDistY = 30; // Minimum distance between two lines + const uint minDistX = 40; + const uint textDistX = 10; + const uint textDistY = 25; + const uint topDist = m_innerScopeRect.top() - m_scopeRect.top(); + const uint leftDist = m_innerScopeRect.left() - m_scopeRect.left(); + const uint mouseX = m_mousePos.x() - m_innerScopeRect.left(); + const uint mouseY = m_mousePos.y() - m_innerScopeRect.top(); + bool hideText; + + QImage hud(m_scopeRect.size(), QImage::Format_ARGB32); + hud.fill(qRgba(0,0,0,0)); + + QPainter davinci(&hud); + davinci.setPen(AbstractScopeWidget::penLight); + + + // Frame display + for (int frameNumber = 0; frameNumber < m_innerScopeRect.height(); frameNumber += minDistY) { + y = topDist + m_innerScopeRect.height()-1 - frameNumber; + hideText = m_mouseWithinWidget && abs(y - mouseY) < textDistY && mouseY < m_innerScopeRect.height() && mouseX < m_innerScopeRect.width(); + + davinci.drawLine(leftDist, y, leftDist + m_innerScopeRect.width()-1, y); + if (!hideText) { + davinci.drawText(leftDist + m_innerScopeRect.width() + textDistX, y + 6, QVariant(frameNumber).toString()); + } + } + // Draw a line through the mouse position with the correct Frame number + if (m_mouseWithinWidget && mouseY < m_innerScopeRect.height() && mouseX < m_innerScopeRect.width()) { + davinci.setPen(AbstractScopeWidget::penLighter); + + x = leftDist + mouseX; + y = topDist + mouseY - 20; + if (y < 0) { + y = 0; + } + if (y > topDist + m_innerScopeRect.height()-1 - 30) { + y = topDist + m_innerScopeRect.height()-1 - 30; + } + davinci.drawLine(x, topDist + mouseY, leftDist + m_innerScopeRect.width()-1, topDist + mouseY); + davinci.drawText(leftDist + m_innerScopeRect.width() + textDistX, + y, + m_scopeRect.right()-m_innerScopeRect.right()-textDistX, + 40, + Qt::AlignLeft, + i18n("Frame\n%1", m_innerScopeRect.height()-1-mouseY)); + } + + // Frequency grid + const uint hzDiff = ceil( ((float)minDistX)/m_innerScopeRect.width() * m_freqMax / 1000 ) * 1000; + const int rightBorder = leftDist + m_innerScopeRect.width()-1; + x = 0; + y = topDist + m_innerScopeRect.height() + textDistY; + for (uint hz = 0; x <= rightBorder; hz += hzDiff) { + davinci.setPen(AbstractScopeWidget::penLight); + x = leftDist + (m_innerScopeRect.width()-1) * ((float)hz)/m_freqMax; + + // Hide text if it would overlap with the text drawn at the mouse position + hideText = m_mouseWithinWidget && abs(x-(leftDist + mouseX + 18)) < minDistX && mouseX < m_innerScopeRect.width(); + + if (x <= rightBorder) { + davinci.drawLine(x, topDist, x, topDist + m_innerScopeRect.height()+6); + } + if (x+textDistY < leftDist + m_innerScopeRect.width()) { + // Only draw the text label if there is still enough room for the final one at the right. + if (!hideText) { + davinci.drawText(x-4, y, QVariant(hz/1000).toString()); + } + } + + + if (hz > 0) { + // 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); + } + } + } + // Draw the line at the very right (maximum frequency) + x = leftDist + m_innerScopeRect.width()-1; + hideText = m_mouseWithinWidget && abs(x-(leftDist + mouseX + 24)) < minDistX && mouseX < m_innerScopeRect.width(); + davinci.drawLine(x, topDist, x, topDist + m_innerScopeRect.height()+6); + if (!hideText) { + davinci.drawText(x-10, y, i18n("%1 kHz").arg((double)m_freqMax/1000, 0, 'f', 1)); + } + + // Draw a line through the mouse position with the correct frequency label + if (m_mouseWithinWidget && mouseX < m_innerScopeRect.width()) { + davinci.setPen(AbstractScopeWidget::penThin); + x = leftDist + mouseX; + davinci.drawLine(x, topDist, x, topDist + m_innerScopeRect.height()+6); + davinci.drawText(x-10, y, i18n("%1 kHz").arg((double)(m_mousePos.x()-m_innerScopeRect.left())/m_innerScopeRect.width() * m_freqMax/1000, 0, 'f', 1)); + } + + // Draw the dB brightness scale + float val; + davinci.setPen(AbstractScopeWidget::penLighter); + for (y = topDist; y < topDist + m_innerScopeRect.height(); y++) { + val = 1-((float)y-topDist)/(m_innerScopeRect.height()-1); + int col = qRgba(255, 255, 255, 255.0 * val); + for (x = leftDist-6; x >= leftDist-13; x--) { + hud.setPixel(x, y, col); + } + } + const int rectWidth = leftDist-m_scopeRect.left()-22; + const int rectHeight = 50; + davinci.setFont(QFont(QFont().defaultFamily(), 10)); + davinci.drawText(m_scopeRect.left(), topDist, rectWidth, rectHeight, Qt::AlignRight, i18n("%1\ndB", m_dBmax)); + davinci.drawText(m_scopeRect.left(), topDist + m_innerScopeRect.height()-20, rectWidth, rectHeight, Qt::AlignRight, i18n("%1\ndB", m_dBmin)); + + + emit signalHUDRenderingFinished(start.elapsed(), 1); + return hud; +} QImage Spectrogram::renderAudioScope(uint, const QVector audioFrame, const int freq, - const int num_channels, const int num_samples) { + const int num_channels, const int num_samples, const int newData) { if (audioFrame.size() > 63) { if (!m_customFreq) { m_freqMax = freq / 2; } + bool newDataAvailable = newData > 0; + +#ifdef DEBUG_SPECTROGRAM + qDebug() << "New data for " << widgetName() << ": " << newDataAvailable << " (" << newData << " units)"; +#endif QTime start = QTime::currentTime(); @@ -139,16 +291,26 @@ QImage Spectrogram::renderAudioScope(uint, const QVector audioFrame, co // Show the window size used, for information ui->labelFFTSizeNumber->setText(QVariant(fftWindow).toString()); + if (newDataAvailable) { - // Get the spectral power distribution of the input samples, - // using the given window size and function - float freqSpectrum[fftWindow/2]; - FFTTools::WindowType windowType = (FFTTools::WindowType) ui->windowFunction->itemData(ui->windowFunction->currentIndex()).toInt(); - m_fftTools.fftNormalized(audioFrame, 0, num_channels, freqSpectrum, windowType, fftWindow, 0); + float freqSpectrum[fftWindow/2]; - QVector spectrumVector(fftWindow/2); - memcpy(spectrumVector.data(), &freqSpectrum[0], fftWindow/2 * sizeof(float)); - m_fftHistory.prepend(spectrumVector); + // Get the spectral power distribution of the input samples, + // using the given window size and function + FFTTools::WindowType windowType = (FFTTools::WindowType) ui->windowFunction->itemData(ui->windowFunction->currentIndex()).toInt(); + m_fftTools.fftNormalized(audioFrame, 0, num_channels, freqSpectrum, windowType, fftWindow, 0); + + // This methid might be called also when a simple refresh is required. + // In this case there is no data to append to the history. Only append new data. + QVector spectrumVector(fftWindow/2); + memcpy(spectrumVector.data(), &freqSpectrum[0], fftWindow/2 * sizeof(float)); + m_fftHistory.prepend(spectrumVector); + } +#ifdef DEBUG_SPECTROGRAM + else { + qDebug() << widgetName() << ": Has no new data to Fourier-transform"; + } +#endif // Limit the maximum history size to avoid wasting space while (m_fftHistory.size() > SPECTROGRAM_HISTORY_SIZE) { @@ -169,80 +331,92 @@ QImage Spectrogram::renderAudioScope(uint, const QVector audioFrame, co float val; uint windowSize; uint xi; - uint y = topDist; + uint y; bool completeRedraw = true; - if (m_fftHistoryImg.size() == m_scopeRect.size()) { - // The size of the widget has not changed since last time, so we can re-use it, - // shift it by one pixel, and render the single remaining line. Usually about + if (m_fftHistoryImg.size() == m_scopeRect.size() && !m_parameterChanged) { + // The size of the widget and the parameters (like min/max dB) have not changed since last time, + // so we can re-use it, shift it by one pixel, and render the single remaining line. Usually about // 10 times faster for a widget height of around 400 px. - davinci.drawImage(0, -1, m_fftHistoryImg); + if (newDataAvailable) { + davinci.drawImage(0, -1, m_fftHistoryImg); + } else { + // spectrum = m_fftHistoryImg does NOT work, leads to segfaults (anyone knows why, please tell me) + davinci.drawImage(0, 0, m_fftHistoryImg); + } completeRedraw = false; } - for (QList >::iterator it = m_fftHistory.begin(); it != m_fftHistory.end(); it++) { + y = 0; + if (newData || m_parameterChanged) { + m_parameterChanged = false; - windowSize = (*it).size(); + for (QList >::iterator it = m_fftHistory.begin(); it != m_fftHistory.end(); it++) { - for (uint i = 0; i < w; i++) { + windowSize = (*it).size(); - // i: Pixel coordinate - // f: Target frequency - // x: Frequency array index (float!) corresponding to the pixel - // xi: floor(x) - // val: dB value at position x (Range: [-inf,0]) + for (uint i = 0; i < w; i++) { - f = i/((float) w-1.0) * m_freqMax; - x = 2*f/freq * (windowSize - 1); - xi = (int) floor(x); + // i: Pixel coordinate + // f: Target frequency + // x: Frequency array index (float!) corresponding to the pixel + // xi: floor(x) + // val: dB value at position x (Range: [-inf,0]) - if (x >= windowSize) { - break; - } + f = i/((float) w-1.0) * m_freqMax; + x = 2*f/freq * (windowSize - 1); + xi = (int) floor(x); + + if (x >= windowSize) { + break; + } - // Use linear interpolation in order to get smoother display - if (i == 0 || xi == windowSize-1) { - // ... except if we are at the left or right border of the display or the spectrum - val = (*it)[xi]; - } else { - - if ((*it)[xi] > (*it)[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. + // Use linear interpolation in order to get smoother display + if (i == 0 || xi == windowSize-1) { + // ... except if we are at the left or right border of the display or the spectrum val = (*it)[xi]; } else { - val = (xi+1 - x) * (*it)[xi] - + (x - xi) * (*it)[xi+1]; + + if ((*it)[xi] > (*it)[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. + val = (*it)[xi]; + } else { + val = (xi+1 - x) * (*it)[xi] + + (x - xi) * (*it)[xi+1]; + } } - } - // Normalize to [0 1], 1 corresponding to 0 dB and 0 to dbMin dB - val = -val/m_dBmin + 1; - if (val < 0) { - val = 0; - } + // Normalize to [0 1], 1 corresponding to 0 dB and 0 to dbMin dB + val = (val-m_dBmax)/(m_dBmax-m_dBmin) + 1; + if (val < 0) { + val = 0; + } else if (val > 1) { + val = 1; + } - spectrum.setPixel(leftDist + i, topDist + h-y-1, qRgba(255, 255, 255, val * 255)); + spectrum.setPixel(leftDist + i, topDist + h-1 - y, qRgba(255, 255, 255, val * 255)); - x_prev = x; - } + x_prev = x; + } - y++; - if (y >= topDist + m_innerScopeRect.height()) { - break; - } - if (!completeRedraw) { - break; + y++; + if (y >= topDist + m_innerScopeRect.height()) { + break; + } + if (!completeRedraw) { + break; + } } } #ifdef DEBUG_SPECTROGRAM qDebug() << "Rendered " << y-topDist << "lines from " << m_fftHistory.size() << " available samples in " << start.elapsed() << " ms" - << (completeRedraw ? " (re-used old image)" : ""); + << (completeRedraw ? "" : " (re-used old image)"); uint storedBytes = 0; for (QList< QVector >::iterator it = m_fftHistory.begin(); it != m_fftHistory.end(); it++) { storedBytes += (*it).size() * sizeof((*it)[0]); @@ -262,9 +436,86 @@ QImage Spectrogram::renderAudioScope(uint, const QVector audioFrame, co QImage Spectrogram::renderBackground(uint) { return QImage(); } bool Spectrogram::isHUDDependingOnInput() const { return false; } -bool Spectrogram::isScopeDependingOnInput() const { return false; } +bool Spectrogram::isScopeDependingOnInput() const { return true; } bool Spectrogram::isBackgroundDependingOnInput() const { return false; } +void Spectrogram::handleMouseDrag(const QPoint movement, const RescaleDirection rescaleDirection, const Qt::KeyboardModifiers rescaleModifiers) +{ + if (rescaleDirection == North) { + // Nort-South direction: Adjust the dB scale + + if ((rescaleModifiers & Qt::ShiftModifier) == 0) { + + // By default adjust the min dB value + m_dBmin += movement.y(); + + } else { + + // Adjust max dB value if Shift is pressed. + m_dBmax += movement.y(); + + } + + // Ensure the dB values lie in [-100, 0] (or rather [MIN_DB_VALUE, 0]) + // 0 is the upper bound, everything below -70 dB is most likely noise + if (m_dBmax > 0) { + m_dBmax = 0; + } + if (m_dBmin < MIN_DB_VALUE) { + m_dBmin = MIN_DB_VALUE; + } + // Ensure there is at least 6 dB between the minimum and the maximum value; + // lower values hardly make sense + if (m_dBmax - m_dBmin < 6) { + if ((rescaleModifiers & Qt::ShiftModifier) == 0) { + // min was adjusted; Try to adjust the max value to maintain the + // minimum dB difference of 6 dB + m_dBmax = m_dBmin + 6; + if (m_dBmax > 0) { + m_dBmax = 0; + m_dBmin = -6; + } + } else { + // max was adjusted, adjust min + m_dBmin = m_dBmax - 6; + if (m_dBmin < MIN_DB_VALUE) { + m_dBmin = MIN_DB_VALUE; + m_dBmax = MIN_DB_VALUE+6; + } + } + } + + m_parameterChanged = true; + forceUpdateHUD(); + forceUpdateScope(); + + } else if (rescaleDirection == East) { + // East-West direction: Adjust the maximum frequency + m_freqMax -= 100*movement.x(); + if (m_freqMax < MIN_FREQ_VALUE) { + m_freqMax = MIN_FREQ_VALUE; + } + if (m_freqMax > MAX_FREQ_VALUE) { + m_freqMax = MAX_FREQ_VALUE; + } + m_customFreq = true; + + m_parameterChanged = true; + forceUpdateHUD(); + forceUpdateScope(); + } +} + + + +void Spectrogram::slotResetMaxFreq() +{ + m_customFreq = false; + m_parameterChanged = true; + forceUpdateHUD(); + forceUpdateScope(); +} + #undef SPECTROGRAM_HISTORY_SIZE #ifdef DEBUG_SPECTROGRAM #undef DEBUG_SPECTROGRAM diff --git a/src/audioscopes/spectrogram.h b/src/audioscopes/spectrogram.h index d4c37a17..e8bacb34 100644 --- a/src/audioscopes/spectrogram.h +++ b/src/audioscopes/spectrogram.h @@ -8,6 +8,20 @@ * (at your option) any later version. * ***************************************************************************/ +/** This Spectrogram shows the spectral power distribution of incoming audio samples + over time. See http://en.wikipedia.org/wiki/Spectrogram. + + The Spectrogram makes use of two caches: + * A cached image where only the most recent line needs to be appended instead of + having to recalculate the whole image. A typical speedup factor is 10x. + * A FFT cache storing a history of previous spectral power distributions (i.e. + the Fourier-transformed audio signals). This is used if the user adjusts parameters + like the maximum frequency to display or minimum/maximum signal strength in dB. + All required information is preserved in the FFT history, which would not be the + case for an image (consider re-sizing the widget to 100x100 px and then back to + 800x400 px -- lost is lost). +*/ + #ifndef SPECTROGRAM_H #define SPECTROGRAM_H @@ -31,17 +45,19 @@ protected: ///// Implemented methods ///// QRect scopeRect(); QImage renderHUD(uint accelerationFactor); - QImage renderAudioScope(uint accelerationFactor, const QVector audioFrame, const int freq, const int num_channels, const int num_samples); + QImage renderAudioScope(uint accelerationFactor, const QVector audioFrame, const int freq, const int num_channels, const int num_samples, const int newData); QImage renderBackground(uint accelerationFactor); bool isHUDDependingOnInput() const; bool isScopeDependingOnInput() const; bool isBackgroundDependingOnInput() const; virtual void readConfig(); void writeConfig(); + void handleMouseDrag(const QPoint movement, const RescaleDirection rescaleDirection, const Qt::KeyboardModifiers rescaleModifiers); private: Ui::Spectrogram_UI *ui; FFTTools m_fftTools; + QAction *m_aResetHz; QList > m_fftHistory; QImage m_fftHistoryImg; @@ -52,7 +68,13 @@ private: uint m_freqMax; bool m_customFreq; + bool m_parameterChanged; + QRect m_innerScopeRect; + +private slots: + void slotResetMaxFreq(); + }; #endif // SPECTROGRAM_H diff --git a/src/widgets/spectrogram_ui.ui b/src/widgets/spectrogram_ui.ui index 0c85c7d8..c061538f 100644 --- a/src/widgets/spectrogram_ui.ui +++ b/src/widgets/spectrogram_ui.ui @@ -6,7 +6,7 @@ 0 0 - 400 + 605 300 @@ -33,19 +33,6 @@ - - - - Qt::Horizontal - - - - 40 - 20 - - - - @@ -72,10 +59,23 @@ - + + + + + Qt::Horizontal + + + + 40 + 20 + + + + -- 2.39.5