X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Faudioscopes%2Fspectrogram.cpp;h=bf06833c0142201537c33e3da279affae0759f04;hb=87914857d36c5b051b006f3f74d489af3801af4d;hp=657a896c30347b74ba1a3faa6cffe2178e09074c;hpb=97a640560f43d7f72c783c80ddf4fed419c4adb2;p=kdenlive diff --git a/src/audioscopes/spectrogram.cpp b/src/audioscopes/spectrogram.cpp index 657a896c..bf06833c 100644 --- a/src/audioscopes/spectrogram.cpp +++ b/src/audioscopes/spectrogram.cpp @@ -46,12 +46,15 @@ Spectrogram::Spectrogram(QWidget *parent) : m_aGrid->setCheckable(true); m_aTrackMouse = new QAction(i18n("Track mouse"), this); m_aTrackMouse->setCheckable(true); + m_aHighlightPeaks = new QAction(i18n("Highlight peaks"), this); + m_aHighlightPeaks->setCheckable(true); m_menu->addSeparator(); m_menu->addAction(m_aResetHz); m_menu->addAction(m_aTrackMouse); m_menu->addAction(m_aGrid); + m_menu->addAction(m_aHighlightPeaks); m_menu->removeAction(m_aRealtime); @@ -85,6 +88,7 @@ Spectrogram::~Spectrogram() delete m_aResetHz; delete m_aTrackMouse; delete m_aGrid; + delete ui; } void Spectrogram::readConfig() @@ -98,6 +102,7 @@ void Spectrogram::readConfig() ui->windowFunction->setCurrentIndex(scopeConfig.readEntry("windowFunction", 0)); m_aTrackMouse->setChecked(scopeConfig.readEntry("trackMouse", true)); m_aGrid->setChecked(scopeConfig.readEntry("drawGrid", true)); + m_aHighlightPeaks->setChecked(scopeConfig.readEntry("highlightPeaks", true)); m_dBmax = scopeConfig.readEntry("dBmax", 0); m_dBmin = scopeConfig.readEntry("dBmin", -70); m_freqMax = scopeConfig.readEntry("freqMax", 0); @@ -118,6 +123,7 @@ void Spectrogram::writeConfig() scopeConfig.writeEntry("windowFunction", ui->windowFunction->currentIndex()); scopeConfig.writeEntry("trackMouse", m_aTrackMouse->isChecked()); scopeConfig.writeEntry("drawGrid", m_aGrid->isChecked()); + scopeConfig.writeEntry("highlightPeaks", m_aHighlightPeaks->isChecked()); scopeConfig.writeEntry("dBmax", m_dBmax); scopeConfig.writeEntry("dBmin", m_dBmin); @@ -143,7 +149,7 @@ QRect Spectrogram::scopeRect() ); m_innerScopeRect = QRect( QPoint( - m_scopeRect.left()+56, // Left + m_scopeRect.left()+66, // Left m_scopeRect.top()+6 // Top ), QPoint( ui->verticalSpacer->geometry().right()-70, @@ -155,137 +161,148 @@ QRect Spectrogram::scopeRect() QImage Spectrogram::renderHUD(uint) { + if (m_innerScopeRect.width() > 0 && m_innerScopeRect.height() > 0) { + QTime start = QTime::currentTime(); - 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 int mouseX = m_mousePos.x() - m_innerScopeRect.left(); - const int 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 - if (m_aGrid->isChecked()) { - for (int frameNumber = 0; frameNumber < m_innerScopeRect.height(); frameNumber += minDistY) { - y = topDist + m_innerScopeRect.height()-1 - frameNumber; - hideText = m_aTrackMouse->isChecked() && m_mouseWithinWidget && abs(y - mouseY) < (int)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_aTrackMouse->isChecked() && m_mouseWithinWidget && mouseY < m_innerScopeRect.height() && mouseX < m_innerScopeRect.width()) { - davinci.setPen(AbstractScopeWidget::penLighter); + 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 int mouseX = m_mousePos.x() - m_innerScopeRect.left(); + const int mouseY = m_mousePos.y() - m_innerScopeRect.top(); + bool hideText; - x = leftDist + mouseX; - y = topDist + mouseY - 20; - if (y < 0) { - y = 0; - } - if (y > (int)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)); - } + QImage hud(m_scopeRect.size(), QImage::Format_ARGB32); + hud.fill(qRgba(0,0,0,0)); - // 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; - if (m_aGrid->isChecked()) { - 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_aTrackMouse->isChecked() && m_mouseWithinWidget && abs(x-(leftDist + mouseX + 20)) < (int) minDistX + 16 && 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. + QPainter davinci(&hud); + davinci.setPen(AbstractScopeWidget::penLight); + + + // Frame display + if (m_aGrid->isChecked()) { + for (int frameNumber = 0; frameNumber < m_innerScopeRect.height(); frameNumber += minDistY) { + y = topDist + m_innerScopeRect.height()-1 - frameNumber; + hideText = m_aTrackMouse->isChecked() && m_mouseWithinWidget && abs(y - mouseY) < (int)textDistY && mouseY < m_innerScopeRect.height() + && mouseX < m_innerScopeRect.width() && mouseX >= 0; + + davinci.drawLine(leftDist, y, leftDist + m_innerScopeRect.width()-1, y); if (!hideText) { - davinci.drawText(x-4, y, QVariant(hz/1000).toString()); + 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_aTrackMouse->isChecked() && m_mouseWithinWidget && mouseY < m_innerScopeRect.height() + && mouseX < m_innerScopeRect.width() && mouseX >= 0) { + davinci.setPen(AbstractScopeWidget::penLighter); + + x = leftDist + mouseX; + y = topDist + mouseY - 20; + if (y < 0) { + y = 0; + } + if (y > (int)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; + if (m_aGrid->isChecked()) { + 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_aTrackMouse->isChecked() && m_mouseWithinWidget && abs(x-(leftDist + mouseX + 20)) < (int) minDistX + 16 + && mouseX < m_innerScopeRect.width() && mouseX >= 0; + + 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; + + 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); } - 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_aTrackMouse->isChecked() && m_mouseWithinWidget && abs(x-(leftDist + mouseX + 30)) < (int) minDistX + && mouseX < m_innerScopeRect.width() && mouseX >= 0; + 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 the line at the very right (maximum frequency) - x = leftDist + m_innerScopeRect.width()-1; - hideText = m_aTrackMouse->isChecked() && m_mouseWithinWidget && abs(x-(leftDist + mouseX + 30)) < (int) 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_aTrackMouse->isChecked() && 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', 2)); - } + // Draw a line through the mouse position with the correct frequency label + if (m_aTrackMouse->isChecked() && m_mouseWithinWidget && mouseX < m_innerScopeRect.width() && mouseX >= 0) { + 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', 2)); + } - // Draw the dB brightness scale - float val; - davinci.setPen(AbstractScopeWidget::penLighter); - for (y = topDist; y < (int)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 >= (int)leftDist-13; x--) { - hud.setPixel(x, y, col); + // Draw the dB brightness scale + float val; + davinci.setPen(AbstractScopeWidget::penLighter); + for (y = topDist; y < (int)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 >= (int)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)); + 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; + emit signalHUDRenderingFinished(start.elapsed(), 1); + return hud; + } else { + emit signalHUDRenderingFinished(0, 1); + return QImage(); + } } QImage Spectrogram::renderAudioScope(uint, const QVector audioFrame, const int freq, const int num_channels, const int num_samples, const int newData) { - if (audioFrame.size() > 63) { + if ( + audioFrame.size() > 63 + && m_innerScopeRect.width() > 0 && m_innerScopeRect.height() > 0 + ) { if (!m_customFreq) { m_freqMax = freq / 2; } @@ -338,16 +355,11 @@ QImage Spectrogram::renderAudioScope(uint, const QVector audioFrame, co QImage spectrum(m_scopeRect.size(), QImage::Format_ARGB32); spectrum.fill(qRgba(0,0,0,0)); QPainter davinci(&spectrum); - const uint w = m_innerScopeRect.width(); const uint h = m_innerScopeRect.height(); const uint leftDist = m_innerScopeRect.left() - m_scopeRect.left(); const uint topDist = m_innerScopeRect.top() - m_scopeRect.top(); - float f; - float x; - float x_prev = 0; float val; uint windowSize; - uint xi; uint y; bool completeRedraw = true; @@ -367,58 +379,34 @@ QImage Spectrogram::renderAudioScope(uint, const QVector audioFrame, co y = 0; if (newData || m_parameterChanged) { m_parameterChanged = false; + bool peak = false; + QVector dbMap; + uint right; for (QList >::iterator it = m_fftHistory.begin(); it != m_fftHistory.end(); it++) { windowSize = (*it).size(); - for (uint i = 0; i < w; i++) { + // Interpolate the frequency data to match the pixel coordinates + right = ((float) m_freqMax)/(m_freq/2) * (windowSize - 1); + dbMap = FFTTools::interpolatePeakPreserving((*it), m_innerScopeRect.width(), 0, right, -180); - // 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 (int i = 0; i < dbMap.size(); i++) { + val = dbMap[i]; + peak = val > m_dBmax; - 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. - 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 + // Normalize dB value to [0 1], 1 corresponding to dbMax 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-1 - y, qRgba(255, 255, 255, val * 255)); - - x_prev = x; + if (!peak || !m_aHighlightPeaks->isChecked()) { + spectrum.setPixel(leftDist + i, topDist + h-1 - y, qRgba(255, 255, 255, val * 255)); + } else { + spectrum.setPixel(leftDist + i, topDist + h-1 - y, AbstractScopeWidget::colHighlightDark.rgba()); + } } y++; @@ -456,7 +444,7 @@ bool Spectrogram::isHUDDependingOnInput() 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) +void Spectrogram::handleMouseDrag(const QPoint &movement, const RescaleDirection rescaleDirection, const Qt::KeyboardModifiers rescaleModifiers) { if (rescaleDirection == North) { // Nort-South direction: Adjust the dB scale