Mercurial > hg > svgui
diff layer/SpectrogramLayer.cpp @ 253:1b1e6947c124
* FFT: fix invalid write of normalisation factor in compact mode of disc cache
* FFT: fix range problem for normalisation factor in compact mode (it was
stored as an unsigned scaled from an assumed float range of 0->1, which
is not very plausible and not accurate enough even if true -- use a float
instead)
* Spectrogram: fix vertical zoom behaviour for log frequency spectrograms:
make the thing in the middle of the display remain in the middle after zoom
* Overview widget: don't update the detailed waveform if still decoding the
audio file (too expensive to do all those redraws)
author | Chris Cannam |
---|---|
date | Fri, 08 Jun 2007 15:19:50 +0000 |
parents | 8d89f8869cfb |
children | 11021509c4eb |
line wrap: on
line diff
--- a/layer/SpectrogramLayer.cpp Fri Jun 01 13:56:35 2007 +0000 +++ b/layer/SpectrogramLayer.cpp Fri Jun 08 15:19:50 2007 +0000 @@ -22,6 +22,7 @@ #include "base/Pitch.h" #include "base/Preferences.h" #include "base/RangeMapper.h" +#include "base/LogRange.h" #include "ColourMapper.h" #include <QPainter> @@ -1399,7 +1400,7 @@ if (peaksOnly && !fft->isLocalPeak(s, q)) continue; - if (!fft->isOverThreshold(s, q, m_threshold)) continue; + if (!fft->isOverThreshold(s, q, m_threshold * (m_fftSize/2))) continue; float freq = binfreq; bool steady = false; @@ -1695,6 +1696,9 @@ void SpectrogramLayer::paint(View *v, QPainter &paint, QRect rect) const { + // What a lovely, old-fashioned function this is. + // It's practically FORTRAN 77 in its clarity and linearity. + Profiler profiler("SpectrogramLayer::paint", true); #ifdef DEBUG_SPECTROGRAM_REPAINT std::cerr << "SpectrogramLayer::paint(): m_model is " << m_model << ", zoom level is " << v->getZoomLevel() << ", m_updateTimer " << m_updateTimer << std::endl; @@ -1986,20 +1990,32 @@ // Set minFreq and maxFreq to the frequency extents of the possibly // zero-padded visible bin range, and displayMinFreq and displayMaxFreq // to the actual scale frequency extents (presumably not zero padded). + + // If we are zero padding, we want to use the zero-padded + // equivalents of the bins that we would be using if not zero + // padded, to avoid spaces at the top and bottom of the display. + + // Note fftSize is the actual zero-padded fft size, m_fftSize the + // nominal fft size. - size_t maxbin = fftSize / 2; + size_t maxbin = m_fftSize / 2; if (m_maxFrequency > 0) { - maxbin = int((double(m_maxFrequency) * fftSize) / sr + 0.1); - if (maxbin > fftSize / 2) maxbin = fftSize / 2; + maxbin = int((double(m_maxFrequency) * m_fftSize) / sr + 0.001); + if (maxbin > m_fftSize / 2) maxbin = m_fftSize / 2; } size_t minbin = 1; if (m_minFrequency > 0) { - minbin = int((double(m_minFrequency) * fftSize) / sr + 0.1); + minbin = int((double(m_minFrequency) * m_fftSize) / sr + 0.001); +// std::cerr << "m_minFrequency = " << m_minFrequency << " -> minbin = " << minbin << std::endl; if (minbin < 1) minbin = 1; if (minbin >= maxbin) minbin = maxbin - 1; } + int zpl = getZeroPadLevel(v) + 1; + minbin = minbin * zpl; + maxbin = (maxbin + 1) * zpl - 1; + float minFreq = (float(minbin) * sr) / fftSize; float maxFreq = (float(maxbin) * sr) / fftSize; @@ -2011,6 +2027,8 @@ displayMaxFreq = getEffectiveMaxFrequency(); } +// std::cerr << "(giving actual minFreq " << minFreq << " and display minFreq " << displayMinFreq << ")" << std::endl; + float ymag[h]; float ydiv[h]; float yval[maxbin + 1]; //!!! cache this? @@ -2104,7 +2122,7 @@ } if (m_threshold != 0.f && - !fft->isOverThreshold(s, q, m_threshold)) { + !fft->isOverThreshold(s, q, m_threshold * (m_fftSize/2))) { continue; } @@ -2413,6 +2431,7 @@ { min = getEffectiveMinFrequency(); max = getEffectiveMaxFrequency(); + // std::cerr << "SpectrogramLayer::getDisplayExtents: " << min << "->" << max << std::endl; return true; } @@ -2422,7 +2441,7 @@ { if (!m_model) return false; - std::cerr << "SpectrogramLayer::setDisplayExtents: " << min << "->" << max << std::endl; +// std::cerr << "SpectrogramLayer::setDisplayExtents: " << min << "->" << max << std::endl; if (min < 0) min = 0; if (max > m_model->getSampleRate()/2) max = m_model->getSampleRate()/2; @@ -2956,20 +2975,52 @@ void SpectrogramLayer::setVerticalZoomStep(int step) { - //!!! does not do the right thing for log scale - if (!m_model) return; - float dmin, dmax; - getDisplayExtents(dmin, dmax); + float dmin = m_minFrequency, dmax = m_maxFrequency; +// getDisplayExtents(dmin, dmax); + +// std::cerr << "current range " << dmin << " -> " << dmax << ", range " << dmax-dmin << ", mid " << (dmax + dmin)/2 << std::endl; int sr = m_model->getSampleRate(); SpectrogramRangeMapper mapper(sr, m_fftSize); - float ddist = mapper.getValueForPosition(step); - - float dmid = (dmax + dmin) / 2; - float newmin = dmid - ddist / 2; - float newmax = dmid + ddist / 2; + float newdist = mapper.getValueForPosition(step); + + float newmin, newmax; + + if (m_frequencyScale == LogFrequencyScale) { + + // need to pick newmin and newmax such that + // + // (log(newmin) + log(newmax)) / 2 == logmid + // and + // newmax - newmin = newdist + // + // so log(newmax - newdist) + log(newmax) == 2logmid + // log(newmax(newmax - newdist)) == 2logmid + // newmax.newmax - newmax.newdist == exp(2logmid) + // newmax^2 + (-newdist)newmax + -exp(2logmid) == 0 + // quadratic with a = 1, b = -newdist, c = -exp(2logmid), all known + // + // positive root + // newmax = (newdist + sqrt(newdist^2 + 4exp(2logmid))) / 2 + // + // but logmid = (log(dmin) + log(dmax)) / 2 + // so exp(2logmid) = exp(log(dmin) + log(dmax)) + // = exp(log(dmin.dmax)) + // = dmin.dmax + // so newmax = (newdist + sqrtf(newdist^2 + 4dmin.dmax)) / 2 + + newmax = (newdist + sqrtf(newdist*newdist + 4*dmin*dmax)) / 2; + newmin = newmax - newdist; + +// std::cerr << "newmin = " << newmin << ", newmax = " << newmax << std::endl; + + } else { + float dmid = (dmax + dmin) / 2; + newmin = dmid - newdist / 2; + newmax = dmid + newdist / 2; + } float mmin, mmax; mmin = 0; @@ -2983,10 +3034,10 @@ newmax = mmax; } -// std::cerr << "SpectrogramLayer::setVerticalZoomStep: " << step << ": " << newmin << " -> " << newmax << " (range " << ddist << ")" << std::endl; - - setMinFrequency(int(newmin)); - setMaxFrequency(int(newmax)); +// std::cerr << "SpectrogramLayer::setVerticalZoomStep: " << step << ": " << newmin << " -> " << newmax << " (range " << newdist << ")" << std::endl; + + setMinFrequency(lrintf(newmin)); + setMaxFrequency(lrintf(newmax)); } RangeMapper *