From: Simon A. Eugster Date: Tue, 14 Dec 2010 12:59:54 +0000 (+0000) Subject: Spectrogram: X-Git-Url: https://git.sesse.net/?a=commitdiff_plain;h=85fea3e247282639f22174f8c998fd8f7c4843ec;p=kdenlive Spectrogram: * Re-using interpolation function (moved to FFTTools) * Fixed mouse glitch (see previous commit) svn path=/trunk/kdenlive/; revision=5176 --- diff --git a/src/audioscopes/audiospectrum.cpp b/src/audioscopes/audiospectrum.cpp index e0ce51de..52ba211d 100644 --- a/src/audioscopes/audiospectrum.cpp +++ b/src/audioscopes/audiospectrum.cpp @@ -178,7 +178,7 @@ QImage AudioSpectrum::renderAudioScope(uint, const QVector audioFrame, memcpy(m_lastFFT.data(), &(freqSpectrum[0]), fftWindow/2 * sizeof(float)); uint right = ((float) m_freqMax)/(m_freq/2) * (m_lastFFT.size() - 1); - dbMap = interpolatePeakPreserving(m_lastFFT, m_innerScopeRect.width(), 0, right, -180); + dbMap = FFTTools::interpolatePeakPreserving(m_lastFFT, m_innerScopeRect.width(), 0, right, -180); m_lastFFTLock.release(); @@ -311,7 +311,7 @@ QImage AudioSpectrum::renderHUD(uint) // because the position could already have changed in the meantime (-> crash) if (m_lastFFT.size() > 0 && mouseX >= 0 && mouseX < m_innerScopeRect.width()) { uint right = ((float) m_freqMax)/(m_freq/2) * (m_lastFFT.size() - 1); - QVector dbMap = AudioSpectrum::interpolatePeakPreserving(m_lastFFT, m_innerScopeRect.width(), 0, right, -120); + QVector dbMap = FFTTools::interpolatePeakPreserving(m_lastFFT, m_innerScopeRect.width(), 0, right, -120); db = dbMap[mouseX]; y = topDist + m_innerScopeRect.height()-1 - (dbMap[mouseX] - m_dBmin) / (m_dBmax-m_dBmin) * (m_innerScopeRect.height()-1); @@ -463,78 +463,6 @@ void AudioSpectrum::handleMouseDrag(const QPoint movement, const RescaleDirectio } -const QVector AudioSpectrum::interpolatePeakPreserving(const QVector in, const uint targetSize, uint left, uint right, float fill) -{ -#ifdef DEBUG_AUDIOSPEC - QTime start = QTime::currentTime(); -#endif - - if (right == 0) { - right = in.size()-1; - } - Q_ASSERT(targetSize > 0); - Q_ASSERT(left < right); - - QVector out(targetSize); - - - float x; - float x_prev = 0; - int xi; - uint i; - for (i = 0; i < targetSize; i++) { - - // i: Target index - // x: Interpolated source index (float!) - // xi: floor(x) - - // Transform [0,targetSize-1] to [left,right] - x = ((float) i) / (targetSize-1) * (right-left) + left; - xi = (int) floor(x); - - if (x > in.size()-1) { - // This may happen if right > in.size()-1; Fill the rest of the vector - // with the default value now. - break; - } - - - // Use linear interpolation in order to get smoother display - if (xi == 0 || xi == in.size()-1) { - // ... except if we are at the left or right border of the input sigal. - // Special case here since we consider previous and future values as well for - // the actual interpolation (not possible here). - out[i] = in[xi]; - } else { - if (in[xi] > in[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 (which is at xi). - // (x is the first after the peak if the previous x was smaller than floor(x).) - out[i] = in[xi]; - } else { - out[i] = (xi+1 - x) * in[xi] - + (x - xi) * in[xi+1]; - } - } - x_prev = x; - } - // Fill the rest of the vector if the right border exceeds the input vector. - for (; i < targetSize; i++) { - out[i] = fill; - } - -#ifdef DEBUG_AUDIOSPEC - qDebug() << "Interpolated " << targetSize << " nodes from " << in.size() << " input points in " << start.elapsed() << " ms"; -#endif - - return out; -} - - #ifdef DEBUG_AUDIOSPEC #undef DEBUG_AUDIOSPEC #endif diff --git a/src/audioscopes/audiospectrum.h b/src/audioscopes/audiospectrum.h index 670160c8..2a92cc4c 100644 --- a/src/audioscopes/audiospectrum.h +++ b/src/audioscopes/audiospectrum.h @@ -79,21 +79,6 @@ private: /** The user has chosen a custom frequency. */ bool m_customFreq; - /** This is linear interpolation with the special property that it preserves peaks, which is required - for e.g. showing correct Decibel values (where the peak values are of interest). - Consider f = {0, 100, 0} - x = {0.5, 1.5}: With default linear interpolation x0 and x1 would both be mapped to 50. - This function maps x1 (the first position after the peak) to 100. - - @param in The source vector containing the data - @param targetSize Number of interpolation nodes between ... - @param left the left array index in the in-vector and ... - @param right the right array index (both inclusive). - @param fill If right lies outside of the array bounds (which is perfectly fine here) then this value - will be used for filling the missing information. - */ - static const QVector interpolatePeakPreserving(const QVector in, const uint targetSize, uint left = 0, uint right = 0, float fill = 0.0); - #ifdef DEBUG_AUDIOSPEC long m_timeTotal; long m_showTotal; diff --git a/src/audioscopes/ffttools.cpp b/src/audioscopes/ffttools.cpp index 2ede0fa5..2f8aa37e 100644 --- a/src/audioscopes/ffttools.cpp +++ b/src/audioscopes/ffttools.cpp @@ -228,6 +228,77 @@ void FFTTools::fftNormalized(const QVector audioFrame, const uint chann #endif } +const QVector FFTTools::interpolatePeakPreserving(const QVector in, const uint targetSize, uint left, uint right, float fill) +{ +#ifdef DEBUG_FFTTOOLS + QTime start = QTime::currentTime(); +#endif + + if (right == 0) { + right = in.size()-1; + } + Q_ASSERT(targetSize > 0); + Q_ASSERT(left < right); + + QVector out(targetSize); + + + float x; + float x_prev = 0; + int xi; + uint i; + for (i = 0; i < targetSize; i++) { + + // i: Target index + // x: Interpolated source index (float!) + // xi: floor(x) + + // Transform [0,targetSize-1] to [left,right] + x = ((float) i) / (targetSize-1) * (right-left) + left; + xi = (int) floor(x); + + if (x > in.size()-1) { + // This may happen if right > in.size()-1; Fill the rest of the vector + // with the default value now. + break; + } + + + // Use linear interpolation in order to get smoother display + if (xi == 0 || xi == in.size()-1) { + // ... except if we are at the left or right border of the input sigal. + // Special case here since we consider previous and future values as well for + // the actual interpolation (not possible here). + out[i] = in[xi]; + } else { + if (in[xi] > in[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 (which is at xi). + // (x is the first after the peak if the previous x was smaller than floor(x).) + out[i] = in[xi]; + } else { + out[i] = (xi+1 - x) * in[xi] + + (x - xi) * in[xi+1]; + } + } + x_prev = x; + } + // Fill the rest of the vector if the right border exceeds the input vector. + for (; i < targetSize; i++) { + out[i] = fill; + } + +#ifdef DEBUG_FFTTOOLS + qDebug() << "Interpolated " << targetSize << " nodes from " << in.size() << " input points in " << start.elapsed() << " ms"; +#endif + + return out; +} + #ifdef DEBUG_FFTTOOLS #undef DEBUG_FFTTOOLS #endif diff --git a/src/audioscopes/ffttools.h b/src/audioscopes/ffttools.h index 9014d47e..65a4b2ca 100644 --- a/src/audioscopes/ffttools.h +++ b/src/audioscopes/ffttools.h @@ -56,6 +56,22 @@ public: void fftNormalized(const QVector audioFrame, const uint channel, const uint numChannels, float *freqSpectrum, const WindowType windowType, const uint windowSize, const float param = 0); + + /** This is linear interpolation with the special property that it preserves peaks, which is required + for e.g. showing correct Decibel values (where the peak values are of interest). + Consider f = {0, 100, 0} + x = {0.5, 1.5}: With default linear interpolation x0 and x1 would both be mapped to 50. + This function maps x1 (the first position after the peak) to 100. + + @param in The source vector containing the data + @param targetSize Number of interpolation nodes between ... + @param left the left array index in the in-vector and ... + @param right the right array index (both inclusive). + @param fill If right lies outside of the array bounds (which is perfectly fine here) then this value + will be used for filling the missing information. + */ + static const QVector interpolatePeakPreserving(const QVector in, const uint targetSize, uint left = 0, uint right = 0, float fill = 0.0); + private: QHash m_fftCfgs; // FFT cfg cache QHash > m_windowFunctions; // Window function cache diff --git a/src/audioscopes/spectrogram.cpp b/src/audioscopes/spectrogram.cpp index 5bf9a86b..c1d87c20 100644 --- a/src/audioscopes/spectrogram.cpp +++ b/src/audioscopes/spectrogram.cpp @@ -342,16 +342,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; @@ -372,58 +367,27 @@ QImage Spectrogram::renderAudioScope(uint, const QVector audioFrame, co if (newData || m_parameterChanged) { m_parameterChanged = false; + QVector dbMap; + uint right; for (QList >::iterator it = m_fftHistory.begin(); it != m_fftHistory.end(); it++) { windowSize = (*it).size(); - // TODO use function - 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]; - 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 0 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; } y++; diff --git a/src/audioscopes/spectrogram.h b/src/audioscopes/spectrogram.h index 5e11d9a7..29a94c0a 100644 --- a/src/audioscopes/spectrogram.h +++ b/src/audioscopes/spectrogram.h @@ -68,7 +68,7 @@ private: int m_dBmin; int m_dBmax; - uint m_freqMax; + int m_freqMax; bool m_customFreq; bool m_parameterChanged;