+QImage Spectrogram::renderAudioScope(uint, const QVector<int16_t> audioFrame, const int freq,
+ const int num_channels, const int num_samples) {
+ if (audioFrame.size() > 63) {
+ if (!m_customFreq) {
+ m_freqMax = freq / 2;
+ }
+
+ QTime start = QTime::currentTime();
+
+ 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);
+
+ QVector<float> spectrumVector(fftWindow/2);
+ memcpy(spectrumVector.data(), &freqSpectrum[0], fftWindow/2 * sizeof(float));
+ m_fftHistory.prepend(spectrumVector);
+
+ // Limit the maximum history size to avoid wasting space
+ while (m_fftHistory.size() > SPECTROGRAM_HISTORY_SIZE) {
+ m_fftHistory.removeLast();
+ }
+
+ // 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();
+ float f;
+ float x;
+ float x_prev = 0;
+ float val;
+ uint windowSize;
+ uint xi;
+ uint y = topDist;
+
+ for (QList<QVector<float> >::iterator it = m_fftHistory.begin(); it != m_fftHistory.end(); it++) {
+
+ windowSize = (*it).size();
+
+ for (uint i = 0; i < w; i++) {
+
+ // 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])
+
+ 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
+ val = -val/m_dBmin + 1;
+ if (val < 0) {
+ val = 0;
+ }
+
+ spectrum.setPixel(leftDist + i, topDist + h-y-1, qRgba(225, 182, 255, val * 255));
+
+ x_prev = x;
+ }
+
+ y++;
+ if (y >= topDist + m_innerScopeRect.height()) {
+ break;
+ }
+ }
+
+#ifdef DEBUG_SPECTROGRAM
+ qDebug() << "Rendered " << y-topDist << "lines from " << m_fftHistory.size() << " available samples in " << start.elapsed() << " ms";
+#endif
+
+
+ emit signalScopeRenderingFinished(start.elapsed(), 1);
+ return spectrum;
+ } else {
+ emit signalScopeRenderingFinished(0, 1);
+ return QImage();
+ }