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 "audiospectrum.h" 0009 0010 #include <QElapsedTimer> 0011 #include <QPainter> 0012 0013 #include "klocalizedstring.h" 0014 #include <KConfigGroup> 0015 #include <KSharedConfig> 0016 #include <iostream> 0017 0018 // (defined in the header file) 0019 #ifdef DEBUG_AUDIOSPEC 0020 #include "kdenlive_debug.h" 0021 #endif 0022 0023 // (defined in the header file) 0024 #ifdef DETECT_OVERMODULATION 0025 #include <cmath> 0026 #include <limits> 0027 #endif 0028 0029 // Draw lines instead of single pixels. 0030 // This is about 25 % faster, especially when enlarging the scope to e.g. 1680x1050 px. 0031 #define AUDIOSPEC_LINES 0032 0033 #define MIN_DB_VALUE -120 0034 #define MAX_FREQ_VALUE 96000 0035 #define MIN_FREQ_VALUE 1000 0036 #define ALPHA_MOVING_AVG 0.125 0037 #define MAX_OVM_COLOR 0.7f 0038 0039 AudioSpectrum::AudioSpectrum(QWidget *parent) 0040 : AbstractAudioScopeWidget(true, parent) 0041 , m_fftTools() 0042 , m_lastFFT() 0043 , m_lastFFTLock(1) 0044 , m_peaks() 0045 #ifdef DEBUG_AUDIOSPEC 0046 , m_timeTotal(0) 0047 , m_showTotal(0) 0048 #endif 0049 0050 { 0051 m_ui = new Ui::AudioSpectrum_UI; 0052 m_ui->setupUi(this); 0053 0054 m_aResetHz = new QAction(i18n("Reset maximum frequency to sampling rate"), this); 0055 m_aTrackMouse = new QAction(i18n("Track mouse"), this); 0056 m_aTrackMouse->setCheckable(true); 0057 m_aShowMax = new QAction(i18n("Show maximum"), this); 0058 m_aShowMax->setCheckable(true); 0059 0060 m_menu->addSeparator(); 0061 m_menu->addAction(m_aResetHz); 0062 m_menu->addAction(m_aTrackMouse); 0063 m_menu->addAction(m_aShowMax); 0064 m_menu->removeAction(m_aRealtime); 0065 0066 m_ui->windowSize->addItem(QStringLiteral("256"), QVariant(256)); 0067 m_ui->windowSize->addItem(QStringLiteral("512"), QVariant(512)); 0068 m_ui->windowSize->addItem(QStringLiteral("1024"), QVariant(1024)); 0069 m_ui->windowSize->addItem(QStringLiteral("2048"), QVariant(2048)); 0070 0071 m_ui->windowFunction->addItem(i18n("Rectangular window"), FFTTools::Window_Rect); 0072 m_ui->windowFunction->addItem(i18n("Triangular window"), FFTTools::Window_Triangle); 0073 m_ui->windowFunction->addItem(i18n("Hamming window"), FFTTools::Window_Hamming); 0074 0075 connect(m_aResetHz, &QAction::triggered, this, &AudioSpectrum::slotResetMaxFreq); 0076 0077 connect(m_ui->windowFunction, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, [&](int) { AudioSpectrum::forceUpdate(); }); 0078 connect(this, &AudioSpectrum::signalMousePositionChanged, this, &AudioSpectrum::forceUpdateHUD); 0079 0080 // Note: These strings are used in both Spectogram and AudioSpectrum. Ideally change both (if necessary) to reduce workload on translators 0081 m_ui->labelFFTSize->setToolTip(i18n("The maximum window size is limited by the number of samples per frame.")); 0082 m_ui->windowSize->setToolTip(i18n("A bigger window improves the accuracy at the cost of computational power.")); 0083 m_ui->windowFunction->setToolTip(i18n("The rectangular window function is good for signals with equal signal strength (narrow peak), but creates more " 0084 "smearing. See Window function on Wikipedia.")); 0085 0086 AbstractScopeWidget::init(); 0087 } 0088 AudioSpectrum::~AudioSpectrum() 0089 { 0090 writeConfig(); 0091 0092 delete m_aResetHz; 0093 delete m_aTrackMouse; 0094 delete m_ui; 0095 } 0096 0097 void AudioSpectrum::readConfig() 0098 { 0099 AbstractScopeWidget::readConfig(); 0100 0101 KSharedConfigPtr config = KSharedConfig::openConfig(); 0102 KConfigGroup scopeConfig(config, AbstractScopeWidget::configName()); 0103 0104 m_ui->windowSize->setCurrentIndex(scopeConfig.readEntry("windowSize", 0)); 0105 m_ui->windowFunction->setCurrentIndex(scopeConfig.readEntry("windowFunction", 0)); 0106 m_aTrackMouse->setChecked(scopeConfig.readEntry("trackMouse", true)); 0107 m_aShowMax->setChecked(scopeConfig.readEntry("showMax", true)); 0108 m_dBmax = scopeConfig.readEntry("dBmax", 0); 0109 m_dBmin = scopeConfig.readEntry("dBmin", -70); 0110 m_freqMax = scopeConfig.readEntry("freqMax", 0); 0111 0112 if (m_freqMax == 0) { 0113 m_customFreq = false; 0114 m_freqMax = 10000; 0115 } else { 0116 m_customFreq = true; 0117 } 0118 } 0119 void AudioSpectrum::writeConfig() 0120 { 0121 KSharedConfigPtr config = KSharedConfig::openConfig(); 0122 KConfigGroup scopeConfig(config, AbstractScopeWidget::configName()); 0123 0124 scopeConfig.writeEntry("windowSize", m_ui->windowSize->currentIndex()); 0125 scopeConfig.writeEntry("windowFunction", m_ui->windowFunction->currentIndex()); 0126 scopeConfig.writeEntry("trackMouse", m_aTrackMouse->isChecked()); 0127 scopeConfig.writeEntry("showMax", m_aShowMax->isChecked()); 0128 scopeConfig.writeEntry("dBmax", m_dBmax); 0129 scopeConfig.writeEntry("dBmin", m_dBmin); 0130 if (m_customFreq) { 0131 scopeConfig.writeEntry("freqMax", m_freqMax); 0132 } else { 0133 scopeConfig.writeEntry("freqMax", 0); 0134 } 0135 0136 scopeConfig.sync(); 0137 } 0138 0139 QString AudioSpectrum::widgetName() const 0140 { 0141 return QStringLiteral("AudioSpectrum"); 0142 } 0143 0144 QImage AudioSpectrum::renderBackground(uint) 0145 { 0146 return QImage(); 0147 } 0148 0149 QImage AudioSpectrum::renderAudioScope(uint, const audioShortVector &audioFrame, const int freq, const int num_channels, const int num_samples, const int) 0150 { 0151 if (audioFrame.size() > 63 && m_innerScopeRect.width() > 0 && m_innerScopeRect.height() > 0 // <= 0 if widget is too small (resized by user) 0152 ) { 0153 if (!m_customFreq) { 0154 m_freqMax = freq / 2; 0155 } 0156 0157 QElapsedTimer timer; 0158 timer.start(); 0159 0160 /*******FIXME!!! 0161 #ifdef DETECT_OVERMODULATION 0162 bool overmodulated = false; 0163 int overmodulateCount = 0; 0164 0165 for (int i = 0; i < audioFrame.size(); ++i) { 0166 if ( 0167 audioFrame[i] == std::numeric_limits<qint16>::max() 0168 || audioFrame[i] == std::numeric_limits<qint16>::min()) { 0169 overmodulateCount++; 0170 if (overmodulateCount > 3) { 0171 overmodulated = true; 0172 break; 0173 } 0174 } 0175 } 0176 if (overmodulated) { 0177 m_colorizeFactor = 1; 0178 } else { 0179 if (m_colorizeFactor > 0) { 0180 m_colorizeFactor -= .08; 0181 if (m_colorizeFactor < 0) { 0182 m_colorizeFactor = 0; 0183 } 0184 } 0185 } 0186 #endif 0187 *******/ 0188 0189 // Determine the window size to use. It should be 0190 // * not bigger than the number of samples actually available 0191 // * divisible by 2 0192 int fftWindow = m_ui->windowSize->itemData(m_ui->windowSize->currentIndex()).toInt(); 0193 if (fftWindow > num_samples) { 0194 fftWindow = num_samples; 0195 } 0196 if ((fftWindow & 1) == 1) { 0197 fftWindow--; 0198 } 0199 0200 // Show the window size used, for information 0201 m_ui->labelFFTSizeNumber->setText(QVariant(fftWindow).toString()); 0202 0203 // Get the spectral power distribution of the input samples, 0204 // using the given window size and function 0205 auto *freqSpectrum = new float[uint(fftWindow) / 2]; 0206 FFTTools::WindowType windowType = FFTTools::WindowType(m_ui->windowFunction->itemData(m_ui->windowFunction->currentIndex()).toInt()); 0207 m_fftTools.fftNormalized(audioFrame, 0, uint(num_channels), freqSpectrum, windowType, uint(fftWindow), 0); 0208 0209 // Store the current FFT window (for the HUD) and run the interpolation 0210 // for easy pixel-based dB value access 0211 QVector<float> dbMap; 0212 m_lastFFTLock.acquire(); 0213 m_lastFFT = QVector<float>(fftWindow / 2); 0214 memcpy(m_lastFFT.data(), &(freqSpectrum[0]), uint(fftWindow) / 2 * sizeof(float)); 0215 0216 uint right = uint(m_freqMax / (m_freq / 2.) * (m_lastFFT.size() - 1)); 0217 dbMap = FFTTools::interpolatePeakPreserving(m_lastFFT, uint(m_innerScopeRect.width()), 0, right, -180); 0218 m_lastFFTLock.release(); 0219 0220 #ifdef DEBUG_AUDIOSPEC 0221 QTime drawTime = QTime::currentTime(); 0222 #endif 0223 delete[] freqSpectrum; 0224 // Draw the spectrum 0225 QImage spectrum(m_scopeRect.size(), QImage::Format_ARGB32); 0226 spectrum.fill(qRgba(0, 0, 0, 0)); 0227 const int w = m_innerScopeRect.width(); 0228 const int h = m_innerScopeRect.height(); 0229 const int leftDist = m_innerScopeRect.left() - m_scopeRect.left(); 0230 const int topDist = m_innerScopeRect.top() - m_scopeRect.top(); 0231 QColor spectrumColor(AbstractScopeWidget::colDarkWhite); 0232 int yMax; 0233 0234 #ifdef DETECT_OVERMODULATION 0235 if (m_colorizeFactor > 0) { 0236 QColor col = AbstractScopeWidget::colHighlightDark; 0237 QColor spec = spectrumColor; 0238 float f = std::sin(float(M_PI_2) * m_colorizeFactor); 0239 spectrumColor = QColor(int(f * col.red() + (1.f - f) * spec.red()), int(f * col.green() + (1.f - f) * spec.green()), 0240 int(f * col.blue() + (1.f - f) * spec.blue()), spec.alpha()); 0241 // Limit the maximum colorization for non-overmodulated frames to better 0242 // recognize consecutively overmodulated frames 0243 if (m_colorizeFactor > MAX_OVM_COLOR) { 0244 m_colorizeFactor = MAX_OVM_COLOR; 0245 } 0246 } 0247 #endif 0248 0249 #ifdef AUDIOSPEC_LINES 0250 QPainter davinci; 0251 bool ok = davinci.begin(&spectrum); 0252 if (!ok) { 0253 qDebug() << "Could not initialise QPainter for Audio spectrum."; 0254 return spectrum; 0255 } 0256 davinci.setPen(QPen(QBrush(spectrumColor.rgba()), 1, Qt::SolidLine)); 0257 #endif 0258 0259 for (int i = 0; i < w; ++i) { 0260 yMax = int((dbMap[i] - m_dBmin) / (m_dBmax - m_dBmin) * (h - 1)); 0261 if (yMax < 0) { 0262 yMax = 0; 0263 } else if (yMax >= h) { 0264 yMax = h - 1; 0265 } 0266 #ifdef AUDIOSPEC_LINES 0267 davinci.drawLine(leftDist + i, topDist + h - 1, leftDist + i, topDist + h - 1 - yMax); 0268 #else 0269 for (int y = 0; y < yMax && y < (int)h; ++y) { 0270 spectrum.setPixel(leftDist + i, topDist + h - y - 1, spectrumColor.rgba()); 0271 } 0272 #endif 0273 } 0274 0275 // Calculate the peak values. Use the new value if it is bigger, otherwise adapt to lower 0276 // values using the Moving Average formula 0277 if (m_aShowMax->isChecked()) { 0278 davinci.setPen(QPen(QBrush(AbstractScopeWidget::colHighlightLight), 2)); 0279 if (m_peaks.size() != fftWindow / 2) { 0280 m_peaks = QVector<float>(m_lastFFT); 0281 } else { 0282 for (int i = 0; i < fftWindow / 2; ++i) { 0283 if (m_lastFFT[i] > m_peaks[i]) { 0284 m_peaks[i] = m_lastFFT[i]; 0285 } else { 0286 m_peaks[i] = ALPHA_MOVING_AVG * m_lastFFT[i] + (1 - ALPHA_MOVING_AVG) * m_peaks[i]; 0287 } 0288 } 0289 } 0290 int prev = 0; 0291 m_peakMap = FFTTools::interpolatePeakPreserving(m_peaks, uint(m_innerScopeRect.width()), 0, right, -180); 0292 for (int i = 0; i < w; ++i) { 0293 yMax = int((m_peakMap[i] - m_dBmin) / (m_dBmax - m_dBmin) * (h - 1)); 0294 if (yMax < 0) { 0295 yMax = 0; 0296 } else if (yMax >= h) { 0297 yMax = h - 1; 0298 } 0299 0300 davinci.drawLine(leftDist + i - 1, topDist + h - prev - 1, leftDist + i, topDist + h - yMax - 1); 0301 spectrum.setPixel(leftDist + i, topDist + h - yMax - 1, AbstractScopeWidget::colHighlightLight.rgba()); 0302 prev = yMax; 0303 } 0304 } 0305 0306 #ifdef DEBUG_AUDIOSPEC 0307 m_showTotal++; 0308 m_timeTotal += drawTime.elapsed(); 0309 qCDebug(KDENLIVE_LOG) << widgetName() << " took " << drawTime.elapsed() << " ms for drawing. Average: " << ((float)m_timeTotal / m_showTotal); 0310 #endif 0311 0312 Q_EMIT signalScopeRenderingFinished(uint(timer.elapsed()), 1); 0313 0314 return spectrum; 0315 } 0316 Q_EMIT signalScopeRenderingFinished(0, 1); 0317 return QImage(); 0318 } 0319 QImage AudioSpectrum::renderHUD(uint) 0320 { 0321 QElapsedTimer timer; 0322 timer.start(); 0323 0324 if (m_innerScopeRect.height() > 0 && m_innerScopeRect.width() > 0) { // May be below 0 if widget is too small 0325 0326 // Minimum distance between two lines 0327 const int minDistY = 30; 0328 const int minDistX = 40; 0329 const int textDistX = 10; 0330 const int textDistY = 25; 0331 const int topDist = m_innerScopeRect.top() - m_scopeRect.top(); 0332 const int leftDist = m_innerScopeRect.left() - m_scopeRect.left(); 0333 const int dbDiff = int(ceil((float(minDistY) * (m_dBmax - m_dBmin)) / m_innerScopeRect.height())); 0334 const int mouseX = m_mousePos.x() - m_innerScopeRect.left(); 0335 const int mouseY = m_mousePos.y() - m_innerScopeRect.top(); 0336 0337 QImage hud(m_scopeRect.size(), QImage::Format_ARGB32); 0338 hud.fill(qRgba(0, 0, 0, 0)); 0339 0340 QPainter davinci; 0341 bool ok = davinci.begin(&hud); 0342 if (!ok) { 0343 qDebug() << "Could not initialise QPainter for Audio spectrum HUD."; 0344 return hud; 0345 } 0346 davinci.setPen(AbstractScopeWidget::penLight); 0347 0348 int y; 0349 for (int db = -dbDiff; db > m_dBmin; db -= dbDiff) { 0350 y = topDist + (m_innerScopeRect.height() * db) / (m_dBmin - m_dBmax); 0351 if (y - topDist > m_innerScopeRect.height() - minDistY + 10) { 0352 // Abort here, there is still a line left for min dB to paint which needs some room. 0353 break; 0354 } 0355 davinci.drawLine(leftDist, y, leftDist + m_innerScopeRect.width() - 1, y); 0356 davinci.drawText(leftDist + m_innerScopeRect.width() + textDistX, y + 6, i18n("%1 dB", m_dBmax + db)); 0357 } 0358 davinci.drawLine(leftDist, topDist, leftDist + m_innerScopeRect.width() - 1, topDist); 0359 davinci.drawText(leftDist + m_innerScopeRect.width() + textDistX, topDist + 6, i18n("%1 dB", m_dBmax)); 0360 davinci.drawLine(leftDist, topDist + m_innerScopeRect.height() - 1, leftDist + m_innerScopeRect.width() - 1, topDist + m_innerScopeRect.height() - 1); 0361 davinci.drawText(leftDist + m_innerScopeRect.width() + textDistX, topDist + m_innerScopeRect.height() + 6, i18n("%1 dB", m_dBmin)); 0362 0363 const int hzDiff = int(ceil(float(minDistX) * m_freqMax / m_innerScopeRect.width() / 1000) * 1000); 0364 int x = 0; 0365 const int rightBorder = leftDist + m_innerScopeRect.width() - 1; 0366 y = topDist + m_innerScopeRect.height() + textDistY; 0367 for (int hz = 0; x <= rightBorder; hz += hzDiff) { 0368 davinci.setPen(AbstractScopeWidget::penLighter); 0369 x = leftDist + int(float(m_innerScopeRect.width()) * hz / m_freqMax); 0370 0371 if (x <= rightBorder) { 0372 davinci.drawLine(x, topDist, x, topDist + m_innerScopeRect.height() + 6); 0373 } 0374 if (hz < m_freqMax && x + textDistY < leftDist + m_innerScopeRect.width()) { 0375 davinci.drawText(x - 4, y, QVariant(hz / 1000).toString()); 0376 } else { 0377 x = leftDist + m_innerScopeRect.width(); 0378 davinci.drawLine(x, topDist, x, topDist + m_innerScopeRect.height() + 6); 0379 davinci.drawText(x - 10, y, i18n("%1 kHz", QString("%1").arg(m_freqMax / 1000., 0, 'f', 1))); 0380 } 0381 0382 if (hz > 0) { 0383 // Draw finer lines between the main lines 0384 davinci.setPen(AbstractScopeWidget::penLightDots); 0385 for (uint dHz = 3; dHz > 0; --dHz) { 0386 x = leftDist + int(float(m_innerScopeRect.width()) * (hz - float(dHz) * hzDiff / 4.f) / m_freqMax); 0387 if (x > rightBorder) { 0388 break; 0389 } 0390 davinci.drawLine(x, topDist, x, topDist + m_innerScopeRect.height() - 1); 0391 } 0392 } 0393 } 0394 0395 if (m_aTrackMouse->isChecked() && m_mouseWithinWidget && mouseX < m_innerScopeRect.width() - 1) { 0396 davinci.setPen(AbstractScopeWidget::penThin); 0397 0398 x = leftDist + mouseX; 0399 0400 float db = 0; 0401 float freq = float(mouseX) / (m_innerScopeRect.width() - 1) * m_freqMax; 0402 bool drawDb = false; 0403 0404 m_lastFFTLock.acquire(); 0405 // We need to test whether the mouse is inside the widget 0406 // because the position could already have changed in the meantime (-> crash) 0407 if (!m_lastFFT.isEmpty() && mouseX >= 0 && mouseX < m_innerScopeRect.width()) { 0408 uint right = uint(m_freqMax / (m_freq / 2.f) * (m_lastFFT.size() - 1)); 0409 QVector<float> dbMap = FFTTools::interpolatePeakPreserving(m_lastFFT, uint(m_innerScopeRect.width()), 0, right, -120); 0410 0411 db = dbMap[mouseX]; 0412 y = topDist + m_innerScopeRect.height() - 1 - int((dbMap[mouseX] - m_dBmin) / (m_dBmax - m_dBmin) * (m_innerScopeRect.height() - 1)); 0413 0414 if (y < topDist + m_innerScopeRect.height() - 1) { 0415 drawDb = true; 0416 davinci.drawLine(x, y, leftDist + m_innerScopeRect.width() - 1, y); 0417 } 0418 } else { 0419 y = topDist + mouseY; 0420 } 0421 m_lastFFTLock.release(); 0422 0423 if (y > topDist + mouseY) { 0424 y = topDist + mouseY; 0425 } 0426 davinci.drawLine(x, y, x, topDist + m_innerScopeRect.height() - 1); 0427 0428 if (drawDb) { 0429 QPoint dist(20, -20); 0430 QRect rect(leftDist + mouseX + dist.x(), topDist + mouseY + dist.y(), 100, 40); 0431 if (rect.right() > leftDist + m_innerScopeRect.width() - 1) { 0432 // Mirror the rectangle at the y axis to keep it inside the widget 0433 rect = QRect(rect.topLeft() - QPoint(rect.width() + 2 * dist.x(), 0), rect.size()); 0434 } 0435 0436 QRect textRect(rect.topLeft() + QPoint(12, 4), rect.size()); 0437 0438 davinci.fillRect(rect, AbstractScopeWidget::penBackground.brush()); 0439 davinci.setPen(AbstractScopeWidget::penLighter); 0440 davinci.drawRect(rect); 0441 davinci.drawText(textRect, 0442 QString(i18n("%1 dB", QString("%1").arg(db, 0, 'f', 2)) + '\n' + i18n("%1 kHz", QString("%1").arg(freq / 1000, 0, 'f', 2)))); 0443 } 0444 } 0445 0446 Q_EMIT signalHUDRenderingFinished(uint(timer.elapsed()), 1); 0447 return hud; 0448 } 0449 #ifdef DEBUG_AUDIOSPEC 0450 qCDebug(KDENLIVE_LOG) << "Widget is too small for painting inside. Size of inner scope rect is " << m_innerScopeRect.width() << 'x' 0451 << m_innerScopeRect.height() << "."; 0452 #endif 0453 Q_EMIT signalHUDRenderingFinished(0, 1); 0454 return QImage(); 0455 } 0456 0457 QRect AudioSpectrum::scopeRect() 0458 { 0459 m_scopeRect = QRect(QPoint(10, // Left 0460 m_ui->verticalSpacer->geometry().top() + 6 // Top 0461 ), 0462 AbstractAudioScopeWidget::rect().bottomRight()); 0463 m_innerScopeRect = QRect(QPoint(m_scopeRect.left() + 6, // Left 0464 m_scopeRect.top() + 6 // Top 0465 ), 0466 QPoint(m_ui->verticalSpacer->geometry().right() - 70, m_ui->verticalSpacer->geometry().bottom() - 40)); 0467 return m_scopeRect; 0468 } 0469 0470 void AudioSpectrum::slotResetMaxFreq() 0471 { 0472 m_customFreq = false; 0473 forceUpdateHUD(); 0474 forceUpdateScope(); 0475 } 0476 0477 ///// EVENTS ///// 0478 0479 void AudioSpectrum::handleMouseDrag(const QPoint &movement, const RescaleDirection rescaleDirection, const Qt::KeyboardModifiers rescaleModifiers) 0480 { 0481 if (rescaleDirection == North) { 0482 // Nort-South direction: Adjust the dB scale 0483 0484 if ((rescaleModifiers & Qt::ShiftModifier) == 0) { 0485 0486 // By default adjust the min dB value 0487 m_dBmin += movement.y(); 0488 0489 } else { 0490 0491 // Adjust max dB value if Shift is pressed. 0492 m_dBmax += movement.y(); 0493 } 0494 0495 // Ensure the dB values lie in [-100, 0] (or rather [MIN_DB_VALUE, 0]) 0496 // 0 is the upper bound, everything below -70 dB is most likely noise 0497 if (m_dBmax > 0) { 0498 m_dBmax = 0; 0499 } 0500 if (m_dBmin < MIN_DB_VALUE) { 0501 m_dBmin = MIN_DB_VALUE; 0502 } 0503 // Ensure there is at least 6 dB between the minimum and the maximum value; 0504 // lower values hardly make sense 0505 if (m_dBmax - m_dBmin < 6) { 0506 if ((rescaleModifiers & Qt::ShiftModifier) == 0) { 0507 // min was adjusted; Try to adjust the max value to maintain the 0508 // minimum dB difference of 6 dB 0509 m_dBmax = m_dBmin + 6; 0510 if (m_dBmax > 0) { 0511 m_dBmax = 0; 0512 m_dBmin = -6; 0513 } 0514 } else { 0515 // max was adjusted, adjust min 0516 m_dBmin = m_dBmax - 6; 0517 if (m_dBmin < MIN_DB_VALUE) { 0518 m_dBmin = MIN_DB_VALUE; 0519 m_dBmax = MIN_DB_VALUE + 6; 0520 } 0521 } 0522 } 0523 0524 forceUpdateHUD(); 0525 forceUpdateScope(); 0526 0527 } else if (rescaleDirection == East) { 0528 // East-West direction: Adjust the maximum frequency 0529 m_freqMax -= 100 * movement.x(); 0530 if (m_freqMax < MIN_FREQ_VALUE) { 0531 m_freqMax = MIN_FREQ_VALUE; 0532 } 0533 if (m_freqMax > MAX_FREQ_VALUE) { 0534 m_freqMax = MAX_FREQ_VALUE; 0535 } 0536 m_customFreq = true; 0537 0538 forceUpdateHUD(); 0539 forceUpdateScope(); 0540 } 0541 }