Mercurial > hg > svgui
diff layer/SpectrumLayer.cpp @ 1238:4d0ca1ab4cd0
Some work to make spectrum layers (and slice layers generally) zoomable in the frequency axis. Also fixes a number of view id mixups in SliceLayer which broke offset calculations for the x axis scale.
author | Chris Cannam |
---|---|
date | Tue, 07 Feb 2017 14:55:19 +0000 |
parents | ff97318e993c |
children | 9e1559b08f0d |
line wrap: on
line diff
--- a/layer/SpectrumLayer.cpp Fri Jan 27 13:19:21 2017 +0000 +++ b/layer/SpectrumLayer.cpp Tue Feb 07 14:55:19 2017 +0000 @@ -297,98 +297,29 @@ } } -bool -SpectrumLayer::getValueExtents(double &, double &, bool &, QString &) const +double +SpectrumLayer::getFrequencyForX(const LayerGeometryProvider *v, double x) const { - return false; + if (!m_sliceableModel) return 0; + double bin = getBinForX(v, x); + return (m_sliceableModel->getSampleRate() * bin) / + (m_sliceableModel->getHeight() * 2); } double -SpectrumLayer::getXForBin(int bin, int totalBins, double w) const +SpectrumLayer::getXForFrequency(const LayerGeometryProvider *v, double freq) const { - if (!m_sliceableModel) return SliceLayer::getXForBin(bin, totalBins, w); - - sv_samplerate_t sampleRate = m_sliceableModel->getSampleRate(); - double binfreq = (sampleRate * bin) / (totalBins * 2); - - return getXForFrequency(binfreq, w); -} - -int -SpectrumLayer::getBinForX(double x, int totalBins, double w) const -{ - if (!m_sliceableModel) return SliceLayer::getBinForX(x, totalBins, w); - - sv_samplerate_t sampleRate = m_sliceableModel->getSampleRate(); - double binfreq = getFrequencyForX(x, w); - - return int((binfreq * totalBins * 2) / sampleRate); -} - -double -SpectrumLayer::getFrequencyForX(double x, double w) const -{ - double freq = 0; if (!m_sliceableModel) return 0; - - sv_samplerate_t sampleRate = m_sliceableModel->getSampleRate(); - - double maxfreq = double(sampleRate) / 2; - - switch (m_binScale) { - - case LinearBins: - freq = ((x * maxfreq) / w); - break; - - case LogBins: - freq = pow(10.0, (x * log10(maxfreq)) / w); - break; - - case InvertedLogBins: - freq = maxfreq - pow(10.0, ((w - x) * log10(maxfreq)) / w); - break; - } - - return freq; -} - -double -SpectrumLayer::getXForFrequency(double freq, double w) const -{ - double x = 0; - if (!m_sliceableModel) return x; - - sv_samplerate_t sampleRate = m_sliceableModel->getSampleRate(); - - double maxfreq = double(sampleRate) / 2; - - switch (m_binScale) { - - case LinearBins: - x = (freq * w) / maxfreq; - break; - - case LogBins: - x = (log10(freq) * w) / log10(maxfreq); - break; - - case InvertedLogBins: - if (maxfreq == freq) x = w; - else x = w - (log10(maxfreq - freq) * w) / log10(maxfreq); - break; - } - - return x; + double bin = (freq * m_sliceableModel->getHeight() * 2) / + m_sliceableModel->getSampleRate(); + return getXForBin(v, bin); } bool SpectrumLayer::getXScaleValue(const LayerGeometryProvider *v, int x, double &value, QString &unit) const { - if (m_xorigins.find(v) == m_xorigins.end()) return false; - int xorigin = m_xorigins.find(v)->second; - value = getFrequencyForX(x - xorigin, v->getPaintWidth() - xorigin - 1); + value = getFrequencyForX(v, x); unit = "Hz"; return true; } @@ -397,7 +328,7 @@ SpectrumLayer::getYScaleValue(const LayerGeometryProvider *v, int y, double &value, QString &unit) const { - value = getValueForY(y, v); + value = getValueForY(v, y); if (m_energyScale == dBScale || m_energyScale == MeterScale) { @@ -483,33 +414,34 @@ ColourMapper mapper(m_colourMap, 0, 1); paint.setPen(mapper.getContrastingColour()); - int xorigin = m_xorigins[v]; + int xorigin = m_xorigins[v->getId()]; int w = v->getPaintWidth() - xorigin - 1; paint.drawLine(xorigin, cursorPos.y(), v->getPaintWidth(), cursorPos.y()); paint.drawLine(cursorPos.x(), cursorPos.y(), cursorPos.x(), v->getPaintHeight()); - double fundamental = getFrequencyForX(cursorPos.x() - xorigin, w); + double fundamental = getFrequencyForX(v, cursorPos.x()); int hoffset = 2; if (m_binScale == LogBins) hoffset = 13; PaintAssistant::drawVisibleText(v, paint, - cursorPos.x() + 2, - v->getPaintHeight() - 2 - hoffset, - QString("%1 Hz").arg(fundamental), - PaintAssistant::OutlinedText); + cursorPos.x() + 2, + v->getPaintHeight() - 2 - hoffset, + QString("%1 Hz").arg(fundamental), + PaintAssistant::OutlinedText); if (Pitch::isFrequencyInMidiRange(fundamental)) { QString pitchLabel = Pitch::getPitchLabelForFrequency(fundamental); PaintAssistant::drawVisibleText(v, paint, - cursorPos.x() - paint.fontMetrics().width(pitchLabel) - 2, - v->getPaintHeight() - 2 - hoffset, - pitchLabel, - PaintAssistant::OutlinedText); + cursorPos.x() - + paint.fontMetrics().width(pitchLabel) - 2, + v->getPaintHeight() - 2 - hoffset, + pitchLabel, + PaintAssistant::OutlinedText); } - double value = getValueForY(cursorPos.y(), v); + double value = getValueForY(v, cursorPos.y()); double thresh = m_threshold; double db = thresh; if (value > 0.0) db = 10.0 * log10(value); @@ -531,7 +463,7 @@ while (harmonic < 100) { - int hx = int(lrint(getXForFrequency(fundamental * harmonic, w))); + int hx = int(lrint(getXForFrequency(v, fundamental * harmonic))); hx += xorigin; if (hx < xorigin || hx > v->getPaintWidth()) break; @@ -568,12 +500,15 @@ if (genericDesc == "") return ""; - double minvalue = 0.f; - if (minbin < int(m_values.size())) minvalue = m_values[minbin]; + int i0 = minbin - m_minbin; + int i1 = maxbin - m_minbin; + + float minvalue = 0.0; + if (in_range_for(m_values, i0)) minvalue = m_values[i0]; - double maxvalue = minvalue; - if (maxbin < int(m_values.size())) maxvalue = m_values[maxbin]; - + float maxvalue = minvalue; + if (in_range_for(m_values, i1)) maxvalue = m_values[i1]; + if (minvalue > maxvalue) std::swap(minvalue, maxvalue); QString binstr; @@ -718,10 +653,10 @@ double freq = i->second; - int x = int(lrint(getXForFrequency(freq, w))); + int x = int(lrint(getXForFrequency(v, freq))); double norm = 0.f; - (void)getYForValue(values[bin], v, norm); // don't need return value, need norm + (void)getYForValue(v, values[bin], norm); // don't need return value, need norm paint.setPen(mapper.map(norm)); paint.drawLine(xorigin + x, 0, xorigin + x, v->getPaintHeight() - pkh - 1); @@ -733,87 +668,18 @@ SliceLayer::paint(v, paint, rect); //!!! All of this stuff relating to depicting frequencies - //(keyboard, crosshairs etc) should be applicable to any slice - //layer whose model has a vertical scale unit of Hz. However, the - //dense 3d model at the moment doesn't record its vertical scale - //unit -- we need to fix that and hoist this code as appropriate. - //Same really goes for any code in SpectrogramLayer that could be - //relevant to Colour3DPlotLayer with unit Hz, but that's a bigger - //proposition. + // (keyboard, crosshairs etc) should be applicable to any slice + // layer whose model has a vertical scale unit of Hz. However, + // the dense 3d model at the moment doesn't record its vertical + // scale unit -- we need to fix that and hoist this code as + // appropriate. Same really goes for any code in SpectrogramLayer + // that could be relevant to Colour3DPlotLayer with unit Hz, but + // that's a bigger proposition. -// if (m_binScale == LogBins) { + int h = v->getPaintHeight(); -// int pkh = 10; - int h = v->getPaintHeight(); - - // piano keyboard - - //!!! todo: move to PianoScale::paintPianoHorizontal - - paint.drawLine(xorigin, h - pkh - 1, w + xorigin, h - pkh - 1); - - int px = xorigin, ppx = xorigin; - paint.setBrush(paint.pen().color()); - - for (int i = 0; i < 128; ++i) { - - double f = Pitch::getFrequencyForPitch(i); - int x = int(lrint(getXForFrequency(f, w))); - - x += xorigin; - - if (i == 0) { - px = ppx = x; - } - if (i == 1) { - ppx = px - (x - px); - } - - if (x < xorigin) { - ppx = px; - px = x; - continue; - } - - if (x > w) { - break; - } - - int n = (i % 12); - - if (n == 1) { - // C# -- fill the C from here - QColor col = Qt::gray; - if (i == 61) { // filling middle C - col = Qt::blue; - col = col.light(150); - } - if (x - ppx > 2) { - paint.fillRect((px + ppx) / 2 + 1, - h - pkh, - x - (px + ppx) / 2 - 1, - pkh, - col); - } - } - - if (n == 1 || n == 3 || n == 6 || n == 8 || n == 10) { - // black notes - paint.drawLine(x, h - pkh, x, h); - int rw = int(lrint(double(x - px) / 4) * 2); - if (rw < 2) rw = 2; - paint.drawRect(x - rw/2, h - pkh, rw, pkh/2); - } else if (n == 0 || n == 5) { - // C, F - if (px < w) { - paint.drawLine((x + px) / 2, h - pkh, (x + px) / 2, h); - } - } - - ppx = px; - px = x; - } -// } + PianoScale().paintPianoHorizontal + (v, this, paint, QRect(xorigin, h - pkh - 1, w + xorigin, pkh)); paint.restore(); }