File indexing completed on 2024-04-28 04:52:21
0001 /* 0002 SPDX-FileCopyrightText: 2010 Simon Andreas Eugster <simon.eu@gmail.com> 0003 This file is part of kdenlive. See www.kdenlive.org. 0004 0005 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0006 */ 0007 0008 #include "spectrogram.h" 0009 0010 #include <QDebug> 0011 #include <QElapsedTimer> 0012 #include <QPainter> 0013 0014 #include "klocalizedstring.h" 0015 #include <KConfigGroup> 0016 #include <KSharedConfig> 0017 0018 // Defines the number of FFT samples to store. 0019 // Around 4 kB for a window size of 2000. Should be at least as large as the 0020 // highest vertical screen resolution available for complete reconstruction. 0021 // Can be less as a pre-rendered image is kept in space. 0022 #define SPECTROGRAM_HISTORY_SIZE 1000 0023 0024 // Uncomment for debugging 0025 //#define DEBUG_SPECTROGRAM 0026 0027 #ifdef DEBUG_SPECTROGRAM 0028 #include "kdenlive_debug.h" 0029 #endif 0030 0031 #define MIN_DB_VALUE -120 0032 #define MAX_FREQ_VALUE 96000 0033 #define MIN_FREQ_VALUE 1000 0034 0035 Spectrogram::Spectrogram(QWidget *parent) 0036 : AbstractAudioScopeWidget(true, parent) 0037 , m_fftTools() 0038 , m_fftHistory() 0039 , m_fftHistoryImg() 0040 0041 { 0042 m_ui = new Ui::Spectrogram_UI; 0043 m_ui->setupUi(this); 0044 0045 m_aResetHz = new QAction(i18n("Reset maximum frequency to sampling rate"), this); 0046 m_aGrid = new QAction(i18n("Draw grid"), this); 0047 m_aGrid->setCheckable(true); 0048 m_aTrackMouse = new QAction(i18n("Track mouse"), this); 0049 m_aTrackMouse->setCheckable(true); 0050 m_aHighlightPeaks = new QAction(i18n("Highlight peaks"), this); 0051 m_aHighlightPeaks->setCheckable(true); 0052 0053 m_menu->addSeparator(); 0054 m_menu->addAction(m_aResetHz); 0055 m_menu->addAction(m_aTrackMouse); 0056 m_menu->addAction(m_aGrid); 0057 m_menu->addAction(m_aHighlightPeaks); 0058 m_menu->removeAction(m_aRealtime); 0059 0060 m_ui->windowSize->addItem(QStringLiteral("256"), QVariant(256)); 0061 m_ui->windowSize->addItem(QStringLiteral("512"), QVariant(512)); 0062 m_ui->windowSize->addItem(QStringLiteral("1024"), QVariant(1024)); 0063 m_ui->windowSize->addItem(QStringLiteral("2048"), QVariant(2048)); 0064 0065 m_ui->windowFunction->addItem(i18n("Rectangular window"), FFTTools::Window_Rect); 0066 m_ui->windowFunction->addItem(i18n("Triangular window"), FFTTools::Window_Triangle); 0067 m_ui->windowFunction->addItem(i18n("Hamming window"), FFTTools::Window_Hamming); 0068 0069 // Note: These strings are used in both Spectogram and AudioSpectrum. Ideally change both (if necessary) to reduce workload on translators 0070 m_ui->labelFFTSize->setToolTip(i18n("The maximum window size is limited by the number of samples per frame.")); 0071 m_ui->windowSize->setToolTip(i18n("A bigger window improves the accuracy at the cost of computational power.")); 0072 m_ui->windowFunction->setToolTip(i18n("The rectangular window function is good for signals with equal signal strength (narrow peak), but creates more " 0073 "smearing. See Window function on Wikipedia.")); 0074 0075 connect(m_aResetHz, &QAction::triggered, this, &Spectrogram::slotResetMaxFreq); 0076 connect(m_ui->windowFunction, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, [&](int) { Spectrogram::forceUpdate(); }); 0077 connect(this, &Spectrogram::signalMousePositionChanged, this, &Spectrogram::forceUpdateHUD); 0078 0079 AbstractScopeWidget::init(); 0080 0081 for (int i = 0; i <= 255 / 5; ++i) { 0082 m_colorMap[i + 0 * 255 / 5] = qRgb(0, 0, i * 5); // black to blue 0083 m_colorMap[i + 1 * 255 / 5] = qRgb(0, i * 5, 255); // blue to cyan 0084 m_colorMap[i + 2 * 255 / 5] = qRgb(0, 255, 255 - i * 5); // cyan to green 0085 m_colorMap[i + 3 * 255 / 5] = qRgb(i * 5, 255, 0); // green to yellow 0086 m_colorMap[i + 4 * 255 / 5] = qRgb(255, 255 - i * 5, 0); // yellow to red 0087 } 0088 } 0089 0090 Spectrogram::~Spectrogram() 0091 { 0092 writeConfig(); 0093 0094 delete m_aResetHz; 0095 delete m_aTrackMouse; 0096 delete m_aGrid; 0097 delete m_ui; 0098 } 0099 0100 void Spectrogram::readConfig() 0101 { 0102 AbstractScopeWidget::readConfig(); 0103 0104 KSharedConfigPtr config = KSharedConfig::openConfig(); 0105 KConfigGroup scopeConfig(config, AbstractScopeWidget::configName()); 0106 0107 m_ui->windowSize->setCurrentIndex(scopeConfig.readEntry("windowSize", 0)); 0108 m_ui->windowFunction->setCurrentIndex(scopeConfig.readEntry("windowFunction", 0)); 0109 m_aTrackMouse->setChecked(scopeConfig.readEntry("trackMouse", true)); 0110 m_aGrid->setChecked(scopeConfig.readEntry("drawGrid", true)); 0111 m_aHighlightPeaks->setChecked(scopeConfig.readEntry("highlightPeaks", true)); 0112 m_dBmax = scopeConfig.readEntry("dBmax", 0); 0113 m_dBmin = scopeConfig.readEntry("dBmin", -70); 0114 m_freqMax = scopeConfig.readEntry("freqMax", 0); 0115 0116 if (m_freqMax == 0) { 0117 m_customFreq = false; 0118 m_freqMax = 10000; 0119 } else { 0120 m_customFreq = true; 0121 } 0122 } 0123 void Spectrogram::writeConfig() 0124 { 0125 KSharedConfigPtr config = KSharedConfig::openConfig(); 0126 KConfigGroup scopeConfig(config, AbstractScopeWidget::configName()); 0127 0128 scopeConfig.writeEntry("windowSize", m_ui->windowSize->currentIndex()); 0129 scopeConfig.writeEntry("windowFunction", m_ui->windowFunction->currentIndex()); 0130 scopeConfig.writeEntry("trackMouse", m_aTrackMouse->isChecked()); 0131 scopeConfig.writeEntry("drawGrid", m_aGrid->isChecked()); 0132 scopeConfig.writeEntry("highlightPeaks", m_aHighlightPeaks->isChecked()); 0133 scopeConfig.writeEntry("dBmax", m_dBmax); 0134 scopeConfig.writeEntry("dBmin", m_dBmin); 0135 0136 if (m_customFreq) { 0137 scopeConfig.writeEntry("freqMax", m_freqMax); 0138 } else { 0139 scopeConfig.writeEntry("freqMax", 0); 0140 } 0141 0142 scopeConfig.sync(); 0143 } 0144 0145 QString Spectrogram::widgetName() const 0146 { 0147 return QStringLiteral("Spectrogram"); 0148 } 0149 0150 QRect Spectrogram::scopeRect() 0151 { 0152 m_scopeRect = QRect(QPoint(10, // Left 0153 m_ui->verticalSpacer->geometry().top() + 6 // Top 0154 ), 0155 AbstractAudioScopeWidget::rect().bottomRight()); 0156 m_innerScopeRect = QRect(QPoint(m_scopeRect.left() + 66, // Left 0157 m_scopeRect.top() + 6 // Top 0158 ), 0159 QPoint(m_ui->verticalSpacer->geometry().right() - 70, m_ui->verticalSpacer->geometry().bottom() - 40)); 0160 return m_scopeRect; 0161 } 0162 0163 QImage Spectrogram::renderHUD(uint) 0164 { 0165 if (m_innerScopeRect.width() > 0 && m_innerScopeRect.height() > 0) { 0166 QElapsedTimer timer; 0167 timer.start(); 0168 0169 int x, y; 0170 const int minDistY = 30; // Minimum distance between two lines 0171 const int minDistX = 40; 0172 const int textDistX = 10; 0173 const int textDistY = 25; 0174 const int topDist = m_innerScopeRect.top() - m_scopeRect.top(); 0175 const int leftDist = m_innerScopeRect.left() - m_scopeRect.left(); 0176 const int mouseX = m_mousePos.x() - m_innerScopeRect.left(); 0177 const int mouseY = m_mousePos.y() - m_innerScopeRect.top(); 0178 bool hideText; 0179 0180 QImage hud(m_scopeRect.size(), QImage::Format_ARGB32); 0181 hud.fill(qRgba(0, 0, 0, 0)); 0182 0183 QPainter davinci; 0184 bool ok = davinci.begin(&hud); 0185 if (!ok) { 0186 qDebug() << "Warning: Could not initialise QPainter for Spectrogram HUD."; 0187 return hud; 0188 } 0189 0190 davinci.setPen(AbstractScopeWidget::penLight); 0191 0192 // Frame display 0193 if (m_aGrid->isChecked()) { 0194 for (int frameNumber = 0; frameNumber < m_innerScopeRect.height(); frameNumber += minDistY) { 0195 y = topDist + m_innerScopeRect.height() - 1 - frameNumber; 0196 hideText = m_aTrackMouse->isChecked() && m_mouseWithinWidget && abs(y - mouseY) < textDistY && mouseY < m_innerScopeRect.height() && 0197 mouseX < m_innerScopeRect.width() && mouseX >= 0; 0198 0199 davinci.drawLine(leftDist, y, leftDist + m_innerScopeRect.width() - 1, y); 0200 if (!hideText) { 0201 davinci.drawText(leftDist + m_innerScopeRect.width() + textDistX, y + 6, QVariant(frameNumber).toString()); 0202 } 0203 } 0204 } 0205 // Draw a line through the mouse position with the correct Frame number 0206 if (m_aTrackMouse->isChecked() && m_mouseWithinWidget && mouseY < m_innerScopeRect.height() && mouseX < m_innerScopeRect.width() && mouseX >= 0) { 0207 davinci.setPen(AbstractScopeWidget::penLighter); 0208 0209 x = leftDist + mouseX; 0210 y = topDist + mouseY - 20; 0211 if (y < 0) { 0212 y = 0; 0213 } 0214 if (y > topDist + m_innerScopeRect.height() - 1 - 30) { 0215 y = topDist + m_innerScopeRect.height() - 1 - 30; 0216 } 0217 davinci.drawLine(x, topDist + mouseY, leftDist + m_innerScopeRect.width() - 1, topDist + mouseY); 0218 davinci.drawText(leftDist + m_innerScopeRect.width() + textDistX, y, m_scopeRect.right() - m_innerScopeRect.right() - textDistX, 40, Qt::AlignLeft, 0219 i18n("Frame\n%1", m_innerScopeRect.height() - 1 - mouseY)); 0220 } 0221 0222 // Frequency grid 0223 const uint hzDiff = uint(ceil(float(minDistX) / m_innerScopeRect.width() * m_freqMax / 1000.f) * 1000); 0224 const int rightBorder = leftDist + m_innerScopeRect.width() - 1; 0225 x = 0; 0226 y = topDist + m_innerScopeRect.height() + textDistY; 0227 if (m_aGrid->isChecked()) { 0228 for (uint hz = 0; x <= rightBorder; hz += hzDiff) { 0229 davinci.setPen(AbstractScopeWidget::penLight); 0230 x = int(leftDist + float(m_innerScopeRect.width() - 1) * hz / m_freqMax); 0231 0232 // Hide text if it would overlap with the text drawn at the mouse position 0233 hideText = m_aTrackMouse->isChecked() && m_mouseWithinWidget && abs(x - (leftDist + mouseX + 20)) < minDistX + 16 && 0234 mouseX < m_innerScopeRect.width() && mouseX >= 0; 0235 0236 if (x <= rightBorder) { 0237 davinci.drawLine(x, topDist, x, topDist + m_innerScopeRect.height() + 6); 0238 } 0239 if (x + textDistY < leftDist + m_innerScopeRect.width()) { 0240 // Only draw the text label if there is still enough room for the final one at the right. 0241 if (!hideText) { 0242 davinci.drawText(x - 4, y, QVariant(hz / 1000).toString()); 0243 } 0244 } 0245 0246 if (hz > 0) { 0247 // Draw finer lines between the main lines 0248 davinci.setPen(AbstractScopeWidget::penLightDots); 0249 for (uint dHz = 3; dHz > 0; --dHz) { 0250 x = int(leftDist + m_innerScopeRect.width() * (hz - dHz * hzDiff / 4.0f) / m_freqMax); 0251 if (x > rightBorder) { 0252 break; 0253 } 0254 davinci.drawLine(x, topDist, x, topDist + m_innerScopeRect.height() - 1); 0255 } 0256 } 0257 } 0258 // Draw the line at the very right (maximum frequency) 0259 x = leftDist + m_innerScopeRect.width() - 1; 0260 hideText = m_aTrackMouse->isChecked() && m_mouseWithinWidget && qAbs(x - (leftDist + mouseX + 30)) < minDistX && 0261 mouseX < m_innerScopeRect.width() && mouseX >= 0; 0262 davinci.drawLine(x, topDist, x, topDist + m_innerScopeRect.height() + 6); 0263 if (!hideText) { 0264 davinci.drawText(x - 10, y, i18n("%1 kHz", QString("%1").arg(m_freqMax / 1000., 0, 'f', 1))); 0265 } 0266 } 0267 0268 // Draw a line through the mouse position with the correct frequency label 0269 if (m_aTrackMouse->isChecked() && m_mouseWithinWidget && mouseX < m_innerScopeRect.width() && mouseX >= 0) { 0270 davinci.setPen(AbstractScopeWidget::penThin); 0271 x = leftDist + mouseX; 0272 davinci.drawLine(x, topDist, x, topDist + m_innerScopeRect.height() + 6); 0273 davinci.drawText( 0274 x - 10, y, 0275 i18n("%1 kHz", QString("%1").arg(double(m_mousePos.x() - m_innerScopeRect.left()) / m_innerScopeRect.width() * m_freqMax / 1000, 0, 'f', 2))); 0276 } 0277 0278 // Draw the dB brightness scale 0279 davinci.setPen(AbstractScopeWidget::penLighter); 0280 for (y = topDist; y < topDist + m_innerScopeRect.height(); ++y) { 0281 float val = 1 - (y - topDist) / (m_innerScopeRect.height() - 1.f); 0282 uint col = m_colorMap[int(val * 255)]; 0283 for (x = leftDist - 6; x >= leftDist - 13; --x) { 0284 hud.setPixel(x, y, col); 0285 } 0286 } 0287 const int rectWidth = leftDist - m_scopeRect.left() - 22; 0288 const int rectHeight = 50; 0289 davinci.setFont(QFont(QFont().defaultFamily(), 10)); 0290 davinci.drawText(m_scopeRect.left(), topDist, rectWidth, rectHeight, Qt::AlignRight, i18n("%1\ndB", m_dBmax)); 0291 davinci.drawText(m_scopeRect.left(), topDist + m_innerScopeRect.height() - 20, rectWidth, rectHeight, Qt::AlignRight, i18n("%1\ndB", m_dBmin)); 0292 0293 Q_EMIT signalHUDRenderingFinished(uint(timer.elapsed()), 1); 0294 return hud; 0295 } 0296 Q_EMIT signalHUDRenderingFinished(0, 1); 0297 return QImage(); 0298 } 0299 0300 QImage Spectrogram::renderAudioScope(uint, const audioShortVector &audioFrame, const int freq, const int num_channels, const int num_samples, const int newData) 0301 { 0302 if (audioFrame.size() > 63 && m_innerScopeRect.width() > 0 && m_innerScopeRect.height() > 0) { 0303 if (!m_customFreq) { 0304 m_freqMax = freq / 2; 0305 } 0306 bool newDataAvailable = newData > 0; 0307 0308 #ifdef DEBUG_SPECTROGRAM 0309 qCDebug(KDENLIVE_LOG) << "New data for " << widgetName() << ": " << newDataAvailable << QStringLiteral(" (") << newData << " units)"; 0310 #endif 0311 0312 QElapsedTimer timer; 0313 timer.start(); 0314 0315 int fftWindow = m_ui->windowSize->itemData(m_ui->windowSize->currentIndex()).toInt(); 0316 if (fftWindow > num_samples) { 0317 fftWindow = num_samples; 0318 } 0319 if ((fftWindow & 1) == 1) { 0320 fftWindow--; 0321 } 0322 0323 // Show the window size used, for information 0324 m_ui->labelFFTSizeNumber->setText(QVariant(fftWindow).toString()); 0325 0326 if (newDataAvailable) { 0327 0328 auto *freqSpectrum = new float[uint(fftWindow / 2)]; 0329 0330 // Get the spectral power distribution of the input samples, 0331 // using the given window size and function 0332 FFTTools::WindowType windowType = FFTTools::WindowType(m_ui->windowFunction->itemData(m_ui->windowFunction->currentIndex()).toInt()); 0333 m_fftTools.fftNormalized(audioFrame, 0, uint(num_channels), freqSpectrum, windowType, uint(fftWindow), 0); 0334 0335 // This method might be called also when a simple refresh is required. 0336 // In this case there is no data to append to the history. Only append new data. 0337 QVector<float> spectrumVector(fftWindow / 2); 0338 memcpy(spectrumVector.data(), &freqSpectrum[0], uint(fftWindow) / 2 * sizeof(float)); 0339 m_fftHistory.prepend(spectrumVector); 0340 delete[] freqSpectrum; 0341 } 0342 #ifdef DEBUG_SPECTROGRAM 0343 else { 0344 qCDebug(KDENLIVE_LOG) << widgetName() << ": Has no new data to Fourier-transform"; 0345 } 0346 #endif 0347 0348 // Limit the maximum history size to avoid wasting space 0349 while (m_fftHistory.size() > SPECTROGRAM_HISTORY_SIZE) { 0350 m_fftHistory.removeLast(); 0351 } 0352 0353 // Draw the spectrum 0354 QImage spectrum(m_scopeRect.size(), QImage::Format_ARGB32); 0355 spectrum.fill(qRgba(0, 0, 0, 0)); 0356 0357 QPainter davinci; 0358 bool ok = davinci.begin(&spectrum); 0359 if (!ok) { 0360 qDebug() << "Warning: Could not initialise QPainter for rendering spectrogram."; 0361 return spectrum; 0362 } 0363 0364 const int h = m_innerScopeRect.height(); 0365 const int leftDist = m_innerScopeRect.left() - m_scopeRect.left(); 0366 const int topDist = m_innerScopeRect.top() - m_scopeRect.top(); 0367 int y; 0368 bool completeRedraw = true; 0369 0370 if (m_fftHistoryImg.size() == m_scopeRect.size() && !m_parameterChanged) { 0371 // The size of the widget and the parameters (like min/max dB) have not changed since last time, 0372 // so we can re-use it, shift it by one pixel, and render the single remaining line. Usually about 0373 // 10 times faster for a widget height of around 400 px. 0374 if (newDataAvailable) { 0375 davinci.drawImage(0, -1, m_fftHistoryImg); 0376 } else { 0377 // spectrum = m_fftHistoryImg does NOT work, leads to segfaults (anyone knows why, please tell me) 0378 davinci.drawImage(0, 0, m_fftHistoryImg); 0379 } 0380 completeRedraw = false; 0381 } 0382 0383 y = 0; 0384 if ((newData != 0) || m_parameterChanged) { 0385 m_parameterChanged = false; 0386 0387 QVector<float> dbMap; 0388 uint right; 0389 ////////////////FIXME 0390 for (auto &it : m_fftHistory) { 0391 0392 int windowSize = it.size(); 0393 0394 // Interpolate the frequency data to match the pixel coordinates 0395 right = uint(m_freqMax / (m_freq / 2.f) * (windowSize - 1)); 0396 dbMap = FFTTools::interpolatePeakPreserving(it, uint(m_innerScopeRect.width()), 0, right, -180); 0397 0398 for (int i = 0; i < dbMap.size(); ++i) { 0399 float val; 0400 val = dbMap[i]; 0401 bool peak = val > m_dBmax; 0402 0403 // Normalize dB value to [0 1], 1 corresponding to dbMax dB and 0 to dbMin dB 0404 val = (val - m_dBmax) / (m_dBmax - m_dBmin) + 1.f; 0405 if (val < 0) { 0406 val = 0; 0407 } else if (val > 1) { 0408 val = 1; 0409 } 0410 if (!peak || !m_aHighlightPeaks->isChecked()) { 0411 spectrum.setPixel(leftDist + i, topDist + h - 1 - y, m_colorMap[int(val * 255)]); 0412 } else { 0413 spectrum.setPixel(leftDist + i, topDist + h - 1 - y, AbstractScopeWidget::colHighlightDark.rgba()); 0414 } 0415 } 0416 0417 y++; 0418 if (y >= topDist + m_innerScopeRect.height()) { 0419 break; 0420 } 0421 if (!completeRedraw) { 0422 break; 0423 } 0424 } 0425 } 0426 0427 #ifdef DEBUG_SPECTROGRAM 0428 qCDebug(KDENLIVE_LOG) << "Rendered " << y - topDist << "lines from " << m_fftHistory.size() << " available samples in " << start.elapsed() << " ms" 0429 << (completeRedraw ? "" : " (re-used old image)"); 0430 uint storedBytes = 0; 0431 for (QList<QVector<float>>::iterator it = m_fftHistory.begin(); it != m_fftHistory.end(); ++it) { 0432 storedBytes += (*it).size() * sizeof((*it)[0]); 0433 } 0434 qCDebug(KDENLIVE_LOG) << QString("Total storage used: %1 kB").arg((double)storedBytes / 1000, 0, 'f', 2); 0435 #endif 0436 0437 m_fftHistoryImg = spectrum; 0438 0439 Q_EMIT signalScopeRenderingFinished(uint(timer.elapsed()), 1); 0440 return spectrum; 0441 } 0442 Q_EMIT signalScopeRenderingFinished(0, 1); 0443 return QImage(); 0444 } 0445 QImage Spectrogram::renderBackground(uint) 0446 { 0447 return QImage(); 0448 } 0449 0450 bool Spectrogram::isHUDDependingOnInput() const 0451 { 0452 return false; 0453 } 0454 bool Spectrogram::isScopeDependingOnInput() const 0455 { 0456 return true; 0457 } 0458 bool Spectrogram::isBackgroundDependingOnInput() const 0459 { 0460 return false; 0461 } 0462 0463 void Spectrogram::handleMouseDrag(const QPoint &movement, const RescaleDirection rescaleDirection, const Qt::KeyboardModifiers rescaleModifiers) 0464 { 0465 if (rescaleDirection == North) { 0466 // Nort-South direction: Adjust the dB scale 0467 0468 if ((rescaleModifiers & Qt::ShiftModifier) == 0) { 0469 0470 // By default adjust the min dB value 0471 m_dBmin += movement.y(); 0472 0473 } else { 0474 0475 // Adjust max dB value if Shift is pressed. 0476 m_dBmax += movement.y(); 0477 } 0478 0479 // Ensure the dB values lie in [-100, 0] (or rather [MIN_DB_VALUE, 0]) 0480 // 0 is the upper bound, everything below -70 dB is most likely noise 0481 if (m_dBmax > 0) { 0482 m_dBmax = 0; 0483 } 0484 if (m_dBmin < MIN_DB_VALUE) { 0485 m_dBmin = MIN_DB_VALUE; 0486 } 0487 // Ensure there is at least 6 dB between the minimum and the maximum value; 0488 // lower values hardly make sense 0489 if (m_dBmax - m_dBmin < 6) { 0490 if ((rescaleModifiers & Qt::ShiftModifier) == 0) { 0491 // min was adjusted; Try to adjust the max value to maintain the 0492 // minimum dB difference of 6 dB 0493 m_dBmax = m_dBmin + 6; 0494 if (m_dBmax > 0) { 0495 m_dBmax = 0; 0496 m_dBmin = -6; 0497 } 0498 } else { 0499 // max was adjusted, adjust min 0500 m_dBmin = m_dBmax - 6; 0501 if (m_dBmin < MIN_DB_VALUE) { 0502 m_dBmin = MIN_DB_VALUE; 0503 m_dBmax = MIN_DB_VALUE + 6; 0504 } 0505 } 0506 } 0507 0508 m_parameterChanged = true; 0509 forceUpdateHUD(); 0510 forceUpdateScope(); 0511 0512 } else if (rescaleDirection == East) { 0513 // East-West direction: Adjust the maximum frequency 0514 m_freqMax -= 100 * movement.x(); 0515 if (m_freqMax < MIN_FREQ_VALUE) { 0516 m_freqMax = MIN_FREQ_VALUE; 0517 } 0518 if (m_freqMax > MAX_FREQ_VALUE) { 0519 m_freqMax = MAX_FREQ_VALUE; 0520 } 0521 m_customFreq = true; 0522 0523 m_parameterChanged = true; 0524 forceUpdateHUD(); 0525 forceUpdateScope(); 0526 } 0527 } 0528 0529 void Spectrogram::slotResetMaxFreq() 0530 { 0531 m_customFreq = false; 0532 m_parameterChanged = true; 0533 forceUpdateHUD(); 0534 forceUpdateScope(); 0535 } 0536 0537 void Spectrogram::resizeEvent(QResizeEvent *event) 0538 { 0539 m_parameterChanged = true; 0540 AbstractAudioScopeWidget::resizeEvent(event); 0541 } 0542 0543 #undef SPECTROGRAM_HISTORY_SIZE 0544 #ifdef DEBUG_SPECTROGRAM 0545 #undef DEBUG_SPECTROGRAM 0546 #endif