AbstractScopeWidget::AbstractScopeWidget(bool trackMouse, QWidget *parent) : QWidget(parent) , m_mousePos(0, 0) , m_mouseWithinWidget(false) , offset(5) , m_accelFactorHUD(1) , m_accelFactorScope(1) , m_accelFactorBackground(1) , m_semaphoreHUD(1) , m_semaphoreScope(1) , m_semaphoreBackground(1) , initialDimensionUpdateDone(false) , m_requestForcedUpdate(false) , m_rescaleMinDist(4) , m_rescaleVerticalThreshold(2.0f) , m_rescaleActive(false) , m_rescalePropertiesLocked(false) , m_rescaleFirstRescaleDone(true) , m_rescaleDirection(North) { m_scopePalette = QPalette(); m_scopePalette.setBrush(QPalette::Window, QBrush(dark2)); m_scopePalette.setBrush(QPalette::Base, QBrush(dark)); m_scopePalette.setBrush(QPalette::Button, QBrush(dark)); m_scopePalette.setBrush(QPalette::Text, QBrush(light)); m_scopePalette.setBrush(QPalette::WindowText, QBrush(light)); m_scopePalette.setBrush(QPalette::ButtonText, QBrush(light)); this->setPalette(m_scopePalette); this->setAutoFillBackground(true); m_aAutoRefresh = new QAction(i18n("Auto Refresh"), this); m_aAutoRefresh->setCheckable(true); m_aRealtime = new QAction(i18n("Realtime (with precision loss)"), this); m_aRealtime->setCheckable(true); m_menu = new QMenu(this); // Disabled dark palette on menus since it breaks up with some themes: kdenlive issue #2950 //m_menu->setPalette(m_scopePalette); m_menu->addAction(m_aAutoRefresh); m_menu->addAction(m_aRealtime); this->setContextMenuPolicy(Qt::CustomContextMenu); bool b = true; b &= connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(customContextMenuRequested(QPoint))); b &= connect(this, SIGNAL(signalHUDRenderingFinished(uint, uint)), this, SLOT(slotHUDRenderingFinished(uint, uint))); b &= connect(this, SIGNAL(signalScopeRenderingFinished(uint, uint)), this, SLOT(slotScopeRenderingFinished(uint, uint))); b &= connect(this, SIGNAL(signalBackgroundRenderingFinished(uint, uint)), this, SLOT(slotBackgroundRenderingFinished(uint, uint))); b &= connect(m_aRealtime, SIGNAL(toggled(bool)), this, SLOT(slotResetRealtimeFactor(bool))); b &= connect(m_aAutoRefresh, SIGNAL(toggled(bool)), this, SLOT(slotAutoRefreshToggled(bool))); Q_ASSERT(b); // Enable mouse tracking if desired. // Causes the mouseMoved signal to be emitted when the mouse moves inside the // widget, even when no mouse button is pressed. this->setMouseTracking(trackMouse); }
QImage Waveform::renderGfxScope(uint accelFactor, const QImage &qimage) { QTime start = QTime::currentTime(); start.start(); const int paintmode = ui->paintMode->itemData(ui->paintMode->currentIndex()).toInt(); WaveformGenerator::Rec rec = m_aRec601->isChecked() ? WaveformGenerator::Rec_601 : WaveformGenerator::Rec_709; QImage wave = m_waveformGenerator->calculateWaveform(scopeRect().size() - m_textWidth - QSize(0,m_paddingBottom), qimage, (WaveformGenerator::PaintMode) paintmode, true, rec, accelFactor); emit signalScopeRenderingFinished(start.elapsed(), 1); return wave; }
QImage Histogram::renderGfxScope(uint accelFactor, const QImage qimage) { QTime start = QTime::currentTime(); start.start(); const int componentFlags = (ui->cbY->isChecked() ? 1 : 0) * HistogramGenerator::ComponentY | (ui->cbS->isChecked() ? 1 : 0) * HistogramGenerator::ComponentSum | (ui->cbR->isChecked() ? 1 : 0) * HistogramGenerator::ComponentR | (ui->cbG->isChecked() ? 1 : 0) * HistogramGenerator::ComponentG | (ui->cbB->isChecked() ? 1 : 0) * HistogramGenerator::ComponentB; HistogramGenerator::Rec rec = m_aRec601->isChecked() ? HistogramGenerator::Rec_601 : HistogramGenerator::Rec_709; QImage histogram = m_histogramGenerator->calculateHistogram(m_scopeRect.size(), qimage, componentFlags, rec, m_aUnscaled->isChecked(), accelFactor); emit signalScopeRenderingFinished(start.elapsed(), accelFactor); return histogram; }
QImage AudioSpectrum::renderAudioScope(uint, const QVector<int16_t> audioFrame, const int freq, const int num_channels, const int num_samples, const int) { if ( audioFrame.size() > 63 && m_innerScopeRect.width() > 0 && m_innerScopeRect.height() > 0 // <= 0 if widget is too small (resized by user) ) { if (!m_customFreq) { m_freqMax = freq / 2; } QTime start = QTime::currentTime(); #ifdef DETECT_OVERMODULATION bool overmodulated = false; int overmodulateCount = 0; for (int i = 0; i < audioFrame.size(); i++) { if ( audioFrame[i] == std::numeric_limits<int16_t>::max() || audioFrame[i] == std::numeric_limits<int16_t>::min()) { overmodulateCount++; if (overmodulateCount > 3) { overmodulated = true; break; } } } if (overmodulated) { colorizeFactor = 1; } else { if (colorizeFactor > 0) { colorizeFactor -= .08; if (colorizeFactor < 0) { colorizeFactor = 0; } } } #endif // Determine the window size to use. It should be // * not bigger than the number of samples actually available // * divisible by 2 int fftWindow = ui->windowSize->itemData(ui->windowSize->currentIndex()).toInt(); if (fftWindow > num_samples) { fftWindow = num_samples; } if ((fftWindow & 1) == 1) { fftWindow--; } // Show the window size used, for information ui->labelFFTSizeNumber->setText(QVariant(fftWindow).toString()); // 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); // Store the current FFT window (for the HUD) and run the interpolation // for easy pixel-based dB value access QVector<float> dbMap; m_lastFFTLock.acquire(); m_lastFFT = QVector<float>(fftWindow/2); memcpy(m_lastFFT.data(), &(freqSpectrum[0]), fftWindow/2 * sizeof(float)); uint right = ((float) m_freqMax)/(m_freq/2) * (m_lastFFT.size() - 1); dbMap = FFTTools::interpolatePeakPreserving(m_lastFFT, m_innerScopeRect.width(), 0, right, -180); m_lastFFTLock.release(); #ifdef DEBUG_AUDIOSPEC QTime drawTime = QTime::currentTime(); #endif // Draw the spectrum QImage spectrum(m_scopeRect.size(), QImage::Format_ARGB32); spectrum.fill(qRgba(0,0,0,0)); 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(); QColor spectrumColor(AbstractScopeWidget::colDarkWhite); int yMax; #ifdef DETECT_OVERMODULATION if (colorizeFactor > 0) { QColor col = AbstractScopeWidget::colHighlightDark; QColor spec = spectrumColor; float f = std::sin(M_PI_2 * colorizeFactor); spectrumColor = QColor( (int) (f * col.red() + (1-f) * spec.red()), (int) (f * col.green() + (1-f) * spec.green()), (int) (f * col.blue() + (1-f) * spec.blue()), spec.alpha() ); // Limit the maximum colorization for non-overmodulated frames to better // recognize consecutively overmodulated frames if (colorizeFactor > MAX_OVM_COLOR) { colorizeFactor = MAX_OVM_COLOR; } } #endif #ifdef AUDIOSPEC_LINES QPainter davinci(&spectrum); davinci.setPen(QPen(QBrush(spectrumColor.rgba()), 1, Qt::SolidLine)); #endif for (uint i = 0; i < w; i++) { yMax = (dbMap[i] - m_dBmin) / (m_dBmax-m_dBmin) * (h-1); if (yMax < 0) { yMax = 0; } else if (yMax >= (int)h) { yMax = h-1; } #ifdef AUDIOSPEC_LINES davinci.drawLine(leftDist + i, topDist + h-1, leftDist + i, topDist + h-1 - yMax); #else for (int y = 0; y < yMax && y < (int)h; y++) { spectrum.setPixel(leftDist + i, topDist + h-y-1, spectrumColor.rgba()); } #endif } // Calculate the peak values. Use the new value if it is bigger, otherwise adapt to lower // values using the Moving Average formula if (m_aShowMax->isChecked()) { davinci.setPen(QPen(QBrush(AbstractScopeWidget::colHighlightLight), 2)); if (m_peaks.size() != fftWindow/2) { m_peaks = QVector<float>(m_lastFFT); } else { for (int i = 0; i < fftWindow/2; i++) { if (m_lastFFT[i] > m_peaks[i]) { m_peaks[i] = m_lastFFT[i]; } else { m_peaks[i] = ALPHA_MOVING_AVG * m_lastFFT[i] + (1-ALPHA_MOVING_AVG) * m_peaks[i]; } } } int prev = 0; m_peakMap = FFTTools::interpolatePeakPreserving(m_peaks, m_innerScopeRect.width(), 0, right, -180); for (uint i = 0; i < w; i++) { yMax = (m_peakMap[i] - m_dBmin) / (m_dBmax-m_dBmin) * (h-1); if (yMax < 0) { yMax = 0; } else if (yMax >= (int)h) { yMax = h-1; } davinci.drawLine(leftDist + i-1, topDist + h-prev-1, leftDist + i, topDist + h-yMax-1); spectrum.setPixel(leftDist + i, topDist + h-yMax-1, AbstractScopeWidget::colHighlightLight.rgba()); prev = yMax; } } #ifdef DEBUG_AUDIOSPEC m_showTotal++; m_timeTotal += drawTime.elapsed(); qDebug() << widgetName() << " took " << drawTime.elapsed() << " ms for drawing. Average: " << ((float)m_timeTotal/m_showTotal) ; #endif emit signalScopeRenderingFinished(start.elapsed(), 1); return spectrum; } else { emit signalScopeRenderingFinished(0, 1); return QImage(); } }
QImage AudioSignal::renderAudioScope(uint, const QVector<int16_t> audioFrame, const int, const int num_channels, const int samples, const int) { QTime start = QTime::currentTime(); int num_samples = samples > 200 ? 200 : samples; QByteArray channels; for (int i = 0; i < num_channels; i++) { long val = 0; for (int s = 0; s < num_samples; s ++) { val += abs(audioFrame[i+s*num_channels] / 128); } channels.append(val / num_samples); } if (peeks.count()!=channels.count()){ peeks=QByteArray(channels.count(),0); peekage=QByteArray(channels.count(),0); } for (int chan=0;chan<peeks.count();chan++) { peekage[chan] = peekage[chan]+1; if ( peeks.at(chan)<channels.at(chan) || peekage.at(chan)>50 ) { peekage[chan]=0; peeks[chan]=channels[chan]; } } QImage image(m_scopeRect.size(), QImage::Format_ARGB32); image.fill(Qt::transparent); QPainter p(&image); p.setPen(Qt::white); p.setRenderHint(QPainter::TextAntialiasing, false); p.setRenderHint(QPainter::Antialiasing, false); int numchan = channels.size(); bool horiz=width() > height(); int dbsize=20; bool showdb=width()>(dbsize+40); //valpixel=1.0 for 127, 1.0+(1/40) for 1 short oversample, 1.0+(2/40) for longer oversample for (int i = 0; i < numchan; i++) { //int maxx= (unsigned char)channels[i] * (horiz ? width() : height() ) / 127; double valpixel=valueToPixel((double)(unsigned char)channels[i]/127.0); int maxx= height() * valpixel; int xdelta= height() /42 ; int _y2= (showdb?width()-dbsize:width () ) / numchan - 1 ; int _y1= (showdb?width()-dbsize:width() ) *i/numchan; int _x2= maxx > xdelta ? xdelta - 3 : maxx - 3 ; if (horiz){ dbsize=9; showdb=height()>(dbsize); maxx=width()*valpixel; xdelta = width() / 42; _y2=( showdb?height()-dbsize:height() ) / numchan - 1 ; _y1= (showdb?height()-dbsize:height() ) * i/numchan; _x2= maxx > xdelta ? xdelta - 1 : maxx - 1; } for (int x = 0; x <= 42; x++) { int _x1= x *xdelta; QColor sig=Qt::green; //value of actual painted digit double ival=(double)_x1/(double)xdelta/42.0; if (ival > 40.0/42.0){ sig=Qt::red; }else if ( ival > 37.0/42.0){ sig=Qt::darkYellow; }else if ( ival >30.0/42.0){ sig=Qt::yellow; } if (maxx > 0) { if (horiz){ p.fillRect(_x1, _y1, _x2, _y2, QBrush(sig, Qt::SolidPattern) ); }else{ p.fillRect(_y1, height()-_x1, _y2,-_x2, QBrush(sig, Qt::SolidPattern) ); } maxx -= xdelta; } } int xp=valueToPixel((double)peeks.at(i)/127.0)*(horiz?width():height())-2; p.fillRect(horiz?xp:_y1,horiz?_y1:height()-xdelta-xp,horiz?3:_y2,horiz?_y2:3,QBrush(Qt::gray,Qt::SolidPattern)); } if (showdb){ //draw db value at related pixel for (int l=0;l<dbscale.size();l++){ if (!horiz){ double xf=pow(10.0,(double)dbscale.at(l) / 20.0 )*(double)height(); p.drawText(width()-20,height()-xf*40.0/42.0+20, QString().sprintf("%d",dbscale.at(l))); }else{ double xf=pow(10.0,(double)dbscale.at(l) / 20.0 )*(double)width(); p.drawText(xf*40/42-10,height()-2, QString().sprintf("%d",dbscale.at(l))); } } } p.end(); emit signalScopeRenderingFinished((uint) start.elapsed(), 1); return image; }