Mercurial > hg > svgui
changeset 490:aa477ddcadb5
* Restore (better quality) y-axis interpolation in spectrogram
* Make spectrogram x axis interpolation a preference
author | Chris Cannam |
---|---|
date | Fri, 06 Feb 2009 15:06:23 +0000 |
parents | 363157772fbd |
children | c74e511a3c96 |
files | layer/SpectrogramLayer.cpp layer/SpectrogramLayer.h |
diffstat | 2 files changed, 101 insertions(+), 323 deletions(-) [+] |
line wrap: on
line diff
--- a/layer/SpectrogramLayer.cpp Thu Feb 05 17:46:34 2009 +0000 +++ b/layer/SpectrogramLayer.cpp Fri Feb 06 15:06:23 2009 +0000 @@ -45,7 +45,7 @@ #include <cassert> #include <cmath> -#define DEBUG_SPECTROGRAM_REPAINT 1 +//#define DEBUG_SPECTROGRAM_REPAINT 1 SpectrogramLayer::SpectrogramLayer(Configuration config) : m_model(0), @@ -640,7 +640,12 @@ setWindowType(Preferences::getInstance()->getWindowType()); return; } - if (name == "Spectrogram Smoothing") { + if (name == "Spectrogram Y Smoothing") { + invalidateImageCaches(); + invalidateMagnitudes(); + emit layerParametersChanged(); + } + if (name == "Spectrogram X Smoothing") { invalidateImageCaches(); invalidateMagnitudes(); emit layerParametersChanged(); @@ -1324,17 +1329,11 @@ q0 = v->getFrequencyForY(y, minf, maxf, logarithmic); q1 = v->getFrequencyForY(y - 1, minf, maxf, logarithmic); - // Now map these on to actual bins, using raw FFT size (unsmoothed) - - int b0 = int((q0 * m_fftSize) / sr); - int b1 = int((q1 * m_fftSize) / sr); - - //!!! this is supposed to return fractions-of-bins, as it were, hence the floats - q0 = b0; - q1 = b1; - -// q0 = (b0 * sr) / m_fftSize; -// q1 = (b1 * sr) / m_fftSize; + // Now map these on to ("proportions of") actual bins, using raw + // FFT size (unsmoothed) + + q0 = (q0 * m_fftSize) / sr; + q1 = (q1 * m_fftSize) / sr; return true; } @@ -1356,18 +1355,11 @@ q0 = v->getFrequencyForY(y, minf, maxf, logarithmic); q1 = v->getFrequencyForY(y - 1, minf, maxf, logarithmic); - // Now map these on to actual bins, using zero-padded FFT size if - // appropriate - - int b0 = int((q0 * getFFTSize(v)) / sr); - int b1 = int((q1 * getFFTSize(v)) / sr); - - //!!! this is supposed to return fractions-of-bins, as it were, hence the floats - q0 = b0; - q1 = b1; - -// q0 = (b0 * sr) / m_fftSize; -// q1 = (b1 * sr) / m_fftSize; + // Now map these on to ("proportions of") actual bins, using raw + // FFT size (unsmoothed) + + q0 = (q0 * getFFTSize(v)) / sr; + q1 = (q1 * getFFTSize(v)) / sr; return true; } @@ -2265,7 +2257,7 @@ } int binforx[bufwid]; - int binfory[h]; + float binfory[h]; bool usePeaksCache = false; @@ -2305,7 +2297,7 @@ if (!getSmoothedYBinRange(v, h-y-1, q0, q1)) { binfory[y] = -1; } else { - binfory[y] = int(q0 + 0.0001); + binfory[y] = q0; // cerr << "binfory[" << y << "] = " << binfory[y] << endl; } } @@ -2384,10 +2376,14 @@ << "x" << h << " to " << scaledRight-scaledLeft << "x" << h << endl; #endif + Preferences::SpectrogramXSmoothing xsmoothing = + Preferences::getInstance()->getSpectrogramXSmoothing(); + cerr << "xsmoothing == " << xsmoothing << endl; QImage scaled = m_drawBuffer.scaled (scaledRight - scaledLeft, h, - Qt::IgnoreAspectRatio, Qt::SmoothTransformation); -// cachePainter.setRenderHint(QPainter::SmoothPixmapTransform, true); + Qt::IgnoreAspectRatio, + ((xsmoothing == Preferences::SpectrogramXInterpolated) ? + Qt::SmoothTransformation : Qt::FastTransformation)); int scaledLeftCrop = v->getXForFrame(leftCropFrame); int scaledRightCrop = v->getXForFrame(rightCropFrame); #ifdef DEBUG_SPECTROGRAM_REPAINT @@ -2579,8 +2575,9 @@ << m_columnMags.size() << " at SpectrogramLayer.cpp::paintDrawBuffer" << std::endl; + } else { + m_columnMags[sx].sample(mag); } - m_columnMags[sx].sample(mag); } } } @@ -2593,12 +2590,12 @@ int w, int h, int *binforx, - int *binfory, + float *binfory, bool usePeaksCache) const { Profiler profiler("SpectrogramLayer::paintDrawBuffer"); - int minbin = binfory[0]; + int minbin = int(binfory[0] + 0.0001); int maxbin = binfory[h-1]; #ifdef DEBUG_SPECTROGRAM_REPAINT @@ -2624,8 +2621,20 @@ if (!sourceModel) return false; + bool interpolate = false; + Preferences::SpectrogramSmoothing smoothing = + Preferences::getInstance()->getSpectrogramSmoothing(); + if (smoothing == Preferences::SpectrogramInterpolated || + smoothing == Preferences::SpectrogramZeroPaddedAndInterpolated) { + if (m_binDisplay != PeakBins && + m_binDisplay != PeakFrequencies) { + interpolate = true; + } + } + int psx = -1; - float values[maxbin - minbin + 1]; + float autoarray[maxbin - minbin + 1]; + const float *values = autoarray; DenseThreeDimensionalModel::Column c; float peaks[h]; @@ -2666,11 +2675,11 @@ cerr << "Retrieving column " << sx << " from fft directly" << endl; #endif if (m_colourScale == PhaseColourScale) { - fft->getPhasesAt(sx, values, minbin, maxbin - minbin + 1); + fft->getPhasesAt(sx, autoarray, minbin, maxbin - minbin + 1); } else if (m_normalizeColumns) { - fft->getNormalizedMagnitudesAt(sx, values, minbin, maxbin - minbin + 1); + fft->getNormalizedMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1); } else { - fft->getMagnitudesAt(sx, values, minbin, maxbin - minbin + 1); + fft->getMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1); } } else { #ifdef DEBUG_SPECTROGRAM_REPAINT @@ -2682,41 +2691,42 @@ if (c[y] > columnMax) columnMax = c[y]; } } + values = c.constData() + minbin; } psx = sx; } for (int y = 0; y < h; ++y) { - int sy0 = binfory[y]; - int sy1 = sy0; + float sy0 = binfory[y]; + float sy1 = sy0 + 1; if (y+1 < h) sy1 = binfory[y+1]; - if (sy0 < 0) sy0 = sy1 - 1; - if (sy0 < 0) continue; - if (sy1 <= sy0) sy1 = sy0 + 1; - -// cerr << "sy0 " << sy0 << " sy1 " << sy1 << endl; - - //!!! review -- if we know we're dealing with - //!!! magnitudes here, we can just use peak of the - //!!! float values - - for (int sy = sy0; sy < sy1; ++sy) { - - float value = 0.f; - if (fft) { - if (m_binDisplay == PeakBins) { - if (!fft->isLocalPeak(sx, sy)) continue; - } - value = values[sy - minbin]; - } else { - if (m_binDisplay == PeakBins) { - if (sy == 0 || sy+1 >= c.size() || - c.at(sy) < c.at(sy-1) || - c.at(sy) < c.at(sy+1)) continue; - } - value = c.at(sy); + + float value = 0.f; + + if (interpolate && fabsf(sy1 - sy0) < 1.f) { + + float centre = (sy0 + sy1) / 2; + float dist = (centre - 0.5) - lrintf(centre - 0.5); + int bin = int(centre); + int other = (dist < 0 ? (bin-1) : (bin+1)); + if (bin < minbin) bin = minbin; + if (bin > maxbin) bin = maxbin; + if (other < minbin || other > maxbin) other = bin; + float prop = 1.f - fabsf(dist); + + float v0 = values[bin - minbin]; + float v1 = values[other - minbin]; + if (m_binDisplay == PeakBins) { + if (bin == minbin || bin == maxbin || + v0 < values[bin-minbin-1] || + v0 < values[bin-minbin+1]) v0 = 0.f; + if (other == minbin || other == maxbin || + v1 < values[other-minbin-1] || + v1 < values[other-minbin+1]) v1 = 0.f; } + if (v0 == 0.f && v1 == 0.f) continue; + value = prop * v0 + (1.f - prop) * v1; if (m_colourScale != PhaseColourScale) { if (!m_normalizeColumns) { @@ -2726,20 +2736,33 @@ value *= m_gain; } -/* - if (m_colourScale != PhaseColourScale) { - if (!m_normalizeColumns) { - value /= (m_fftSize/2.f); + peaks[y] = value; + + } else { + + int by0 = int(sy0 + 0.0001); + int by1 = int(sy1 + 0.0001); + if (by1 < by0 + 1) by1 = by0 + 1; + + for (int bin = by0; bin < by1; ++bin) { + + value = values[bin - minbin]; + if (m_binDisplay == PeakBins) { + if (bin == minbin || bin == maxbin || + value < values[bin-minbin-1] || + value < values[bin-minbin+1]) continue; } -//!!! mag.sample(value); - value *= m_gain; + + if (m_colourScale != PhaseColourScale) { + if (!m_normalizeColumns) { + value /= (m_fftSize/2.f); + } + mag.sample(value); + value *= m_gain; + } + + if (value > peaks[y]) peaks[y] = value; //!!! not right for phase! } - - unsigned char pix = getDisplayValue(v, value); - if (pix > peakpix) peakpix = pix; -// cerr <<x<<","<<y<<" -> "<<sx<<","<<sy<<" -> "<<values[sy]<<" -> "<<(int)pix<< endl; -*/ - if (value > peaks[y]) peaks[y] = value; //!!! not right for phase! } } @@ -2749,15 +2772,12 @@ << m_columnMags.size() << " at SpectrogramLayer.cpp::paintDrawBuffer" << std::endl; + } else { + m_columnMags[sx].sample(mag); } - m_columnMags[sx].sample(mag); } } -// if (m_normalizeColumns && columnMax > 0.f) { -// columnGain /= columnMax; -// } - for (int y = 0; y < h; ++y) { float peak = peaks[y]; @@ -2765,10 +2785,6 @@ if (m_colourScale != PhaseColourScale && m_normalizeColumns && columnMax > 0.f) { -// if (!m_normalizeColumns) { -// peak /= (m_fftSize/2.f); -// } -//!!! mag.sample(value); peak /= columnMax; } @@ -2781,244 +2797,6 @@ return true; } -#ifdef NOT_DEFINED -bool -SpectrogramLayer::paintColumnValues(View *v, - FFTModel *fft, - int x0, - int x, - int minbin, - int maxbin, - float displayMinFreq, - float displayMaxFreq, - float xPixelRatio, - const int h, - const float *yforbin) const -{ - float ymag[h]; - float ydiv[h]; - float values[maxbin - minbin + 1]; - - bool logarithmic = (m_frequencyScale == LogFrequencyScale); - - bool interpolate = false; - Preferences::SpectrogramSmoothing smoothing = - Preferences::getInstance()->getSpectrogramSmoothing(); - if (smoothing == Preferences::SpectrogramInterpolated || - smoothing == Preferences::SpectrogramZeroPaddedAndInterpolated) { - if (m_binDisplay != PeakBins && - m_binDisplay != PeakFrequencies) { - interpolate = true; - } - } - - for (int y = 0; y < h; ++y) { - ymag[y] = 0.f; - ydiv[y] = 0.f; - } - - float s0 = 0, s1 = 0; - - if (!getXBinRange(v, x0 + x * xPixelRatio, s0, s1)) { -#ifdef DEBUG_SPECTROGRAM_REPAINT -// std::cerr << "Out of range at " << x0 + x << std::endl; -#endif - assert(x <= m_drawBuffer.width()); - return true; - } - - int s0i = int(s0 + 0.001); - int s1i = int(s1); - - if (s1i >= int(fft->getWidth())) { - if (s0i >= int(fft->getWidth())) { -#ifdef DEBUG_SPECTROGRAM_REPAINT - std::cerr << "Column " << s0i << " out of range" << std::endl; -#endif - return true; - } else { - s1i = s0i; - } - } - - FFTModel::PeakSet peaks; - - for (int s = s0i; s <= s1i; ++s) { - - if (!m_synchronous) { - if (!fft->isColumnAvailable(s)) { -#ifdef DEBUG_SPECTROGRAM_REPAINT - std::cerr << "Met unavailable column at col " << s << std::endl; -#endif - return false; - } - } -/*!!! - if (!fftSuspended) { - fft->suspendWrites(); - fftSuspended = true; - } -*/ - Profiler innerprof2("SpectrogramLayer::paint: 1 data column"); - - MagnitudeRange mag; - - if (m_binDisplay == PeakFrequencies) { - if (s < int(fft->getWidth()) - 1) { - peaks = fft->getPeakFrequencies(FFTModel::AllPeaks, - s, - minbin, maxbin - 1); - } else { - peaks.clear(); - } - } - - if (m_colourScale == PhaseColourScale) { - fft->getPhasesAt(s, values, minbin, maxbin - minbin + 1); - } else if (m_normalizeColumns) { - fft->getNormalizedMagnitudesAt(s, values, minbin, maxbin - minbin + 1); - } else { - fft->getMagnitudesAt(s, values, minbin, maxbin - minbin + 1); - } - - for (size_t q = minbin; q < maxbin; ++q) { - - Profiler innerprof3("SpectrogramLayer::paint: 1 bin"); - - float y0 = yforbin[q + 1 - minbin]; - float y1 = yforbin[q - minbin]; - - if (m_binDisplay == PeakBins) { - if (!fft->isLocalPeak(s, q)) continue; - } - if (m_binDisplay == PeakFrequencies) { - if (peaks.find(q) == peaks.end()) continue; - } - - if (m_threshold != 0.f && - !fft->isOverThreshold(s, q, m_threshold * (m_fftSize/2))) { - continue; - } - - float sprop = 1.f; - if (s == s0i) sprop *= (s + 1) - s0; - if (s == s1i) sprop *= s1 - s; - - if (m_binDisplay == PeakFrequencies) { - y0 = y1 = v->getYForFrequency - (peaks[q], displayMinFreq, displayMaxFreq, logarithmic); - } - - int y0i = int(y0 + 0.001f); - int y1i = int(y1); - - float value = values[q - minbin]; - - if (m_colourScale != PhaseColourScale) { - if (!m_normalizeColumns) { - value /= (m_fftSize/2.f); - } - mag.sample(value); - value *= m_gain; - } - - if (interpolate) { - - int ypi = y0i; - if (q < maxbin - 1) ypi = int(yforbin[q + 2 - minbin]); - - for (int y = ypi; y <= y1i; ++y) { - - if (y < 0 || y >= h) continue; - - float yprop = sprop; - float iprop = yprop; - - if (ypi < y0i && y <= y0i) { - - float half = float(y0i - ypi) / 2.f; - float dist = y - (ypi + half); - - if (dist >= 0) { - iprop = (iprop * dist) / half; - ymag[y] += iprop * value; - } - } else { - if (y1i > y0i) { - - float half = float(y1i - y0i) / 2.f; - float dist = y - (y0i + half); - - if (dist >= 0) { - iprop = (iprop * (half - dist)) / half; - } - } - - ymag[y] += iprop * value; - ydiv[y] += yprop; - } - } - - } else { - - for (int y = y0i; y <= y1i; ++y) { - - if (y < 0 || y >= h) continue; - - float yprop = sprop; - if (y == y0i) yprop *= (y + 1) - y0; - if (y == y1i) yprop *= y1 - y; - - for (int y = y0i; y <= y1i; ++y) { - - if (y < 0 || y >= h) continue; - - float yprop = sprop; - if (y == y0i) yprop *= (y + 1.f) - y0; - if (y == y1i) yprop *= y1 - y; - ymag[y] += yprop * value; - ydiv[y] += yprop; - } - } - } - } - - if (mag.isSet()) { - - if (s >= int(m_columnMags.size())) { - std::cerr << "INTERNAL ERROR: " << s << " >= " - << m_columnMags.size() << " at SpectrogramLayer.cpp:2087" << std::endl; - } - - m_columnMags[s].sample(mag); -/*!!! - if (overallMag.sample(mag)) { - //!!! scaling would change here - overallMagChanged = true; -#ifdef DEBUG_SPECTROGRAM_REPAINT - std::cerr << "Overall mag changed (again?) at column " << s << ", to [" << overallMag.getMin() << "->" << overallMag.getMax() << "]" << std::endl; -#endif - } -*/ - } - } - - for (int y = 0; y < h; ++y) { - - float value = 0.f; - - if (ydiv[y] > 0.0) { - value = ymag[y] / ydiv[y]; - } - - unsigned char pixel = getDisplayValue(v, value); - m_drawBuffer.setPixel(x, y, pixel); - } - - return true; -} -#endif - void SpectrogramLayer::illuminateLocalFeatures(View *v, QPainter &paint) const {
--- a/layer/SpectrogramLayer.h Thu Feb 05 17:46:34 2009 +0000 +++ b/layer/SpectrogramLayer.h Fri Feb 06 15:06:23 2009 +0000 @@ -425,7 +425,7 @@ void invalidateMagnitudes(); bool updateViewMagnitudes(View *v) const; bool paintDrawBuffer(View *v, int w, int h, - int *binforx, int *binfory, + int *binforx, float *binfory, bool usePeaksCache) const; bool paintDrawBufferPeakFrequencies(View *v, int w, int h, int *binforx,