Mercurial > hg > svgui
diff layer/SliceLayer.cpp @ 193:57c2350a8c40
* Add slice layers (so you can display a slice of a colour 3d plot as if it were
a spectrum)
* Make spectrum layer a subclass of slice layer
author | Chris Cannam |
---|---|
date | Fri, 26 Jan 2007 16:59:57 +0000 |
parents | layer/SpectrumLayer.cpp@42118892f428 |
children | d13e209bfa94 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/layer/SliceLayer.cpp Fri Jan 26 16:59:57 2007 +0000 @@ -0,0 +1,572 @@ + +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2006 QMUL. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "SliceLayer.h" + +#include "view/View.h" +#include "base/AudioLevel.h" +#include "base/RangeMapper.h" + +#include <QPainter> +#include <QPainterPath> + +SliceLayer::SliceLayer() : + m_sliceableModel(0), + m_colour(Qt::darkBlue), + m_energyScale(dBScale), + m_samplingMode(SamplePeak), + m_plotStyle(PlotLines), + m_binScale(LinearBins), + m_normalize(false), + m_bias(false); + m_gain(1.0) +{ +} + +SliceLayer::~SliceLayer() +{ + +} + +void +SliceLayer::setSliceableModel(const Model *model) +{ + const DenseThreeDimensionalModel *sliceable = + dynamic_cast<const DenseThreeDimensionalModel *>(model); + + if (model && !sliceable) { + std::cerr << "WARNING: SliceLayer::setSliceableModel(" << model + << "): model is not a DenseThreeDimensionalModel" << std::endl; + } + + if (m_sliceableModel == sliceable) return; + + m_sliceableModel = sliceable; + + connect(m_sliceableModel, SIGNAL(modelChanged()), + this, SIGNAL(modelChanged())); + + connect(m_sliceableModel, SIGNAL(modelChanged(size_t, size_t)), + this, SIGNAL(modelChanged(size_t, size_t))); + + connect(m_sliceableModel, SIGNAL(completionChanged()), + this, SIGNAL(modelCompletionChanged())); + + emit modelReplaced(); +} + +void +SliceLayer::sliceableModelReplaced(const Model *orig, const Model *replacement) +{ + std::cerr << "SliceLayer::sliceableModelReplaced(" << orig << ", " << replacement << ")" << std::endl; + + if (orig == m_sliceableModel) { + setSliceableModel + (dynamic_cast<const DenseThreeDimensionalModel *>(replacement)); + } +} + +void +SliceLayer::modelAboutToBeDeleted(Model *m) +{ + std::cerr << "SliceLayer::modelAboutToBeDeleted(" << m << ")" << std::endl; + + if (m == m_sliceableModel) { + setSliceableModel(0); + } +} + +void +SliceLayer::paint(View *v, QPainter &paint, QRect rect) const +{ + if (!m_sliceableModel) return; + + int w = (v->width() * 2) / 3; + int xorigin = (v->width() / 2) - (w / 2); + + int h = (v->height() * 2) / 3; + int yorigin = (v->height() / 2) + (h / 2); + + paint.save(); + paint.setPen(m_colour); + paint.setRenderHint(QPainter::Antialiasing, false); + + QPainterPath path; + float thresh = -80.f; + + int mh = m_sliceableModel->getHeight(); + + float *values = new float[mh]; + int divisor = 0; + + for (size_t bin = 0; bin < mh; ++bin) { + values[bin] = 0.f; + } + + size_t f0 = v->getCentreFrame(); + int f0x = v->getXForFrame(f0); + size_t f1 = v->getFrameForX(f0x + 1); + + size_t col0 = f0 / m_sliceableModel->getResolution(); + size_t col1 = col0; + if (m_samplingMode != NearestSample) { + col1 = f1 / m_sliceableModel->getResolution(); + } + if (col1 <= col0) col1 = col0 + 1; + + for (size_t col = col0; col < col1; ++col) { + for (size_t bin = 0; bin < mh; ++bin) { + float value = m_sliceableModel->getValueAt(col, bin); + if (m_bias) value *= bin + 1; + if (m_samplingMode == SamplePeak) { + if (value > values[bin]) values[bin] = value; + } else { + values[bin] += value; + } + } + ++divisor; + } + + float max = 0.f; + for (size_t bin = 0; bin < mh; ++bin) { + if (m_samplingMode == SampleMean) values[bin] /= divisor; + if (values[bin] > max) max = values[bin]; + } + if (max != 0.f && m_normalize) { + for (size_t bin = 0; bin < mh; ++bin) { + values[bin] /= max; + } + } + + float py = 0; + float nx = xorigin; + + for (size_t bin = 0; bin < mh; ++bin) { + + float x; + + switch (m_binScale) { + + case LinearBins: + x = nx; + nx = xorigin + (float(w) * (bin + 1)) / mh; + break; + + case LogBins: + x = nx; + nx = xorigin + (float(w) * (log10f(bin + 2) - log10f(1))) / + (log10f(mh + 1) - log10f(1)); + break; + + case InvertedLogBins: + x = nx; + nx = xorigin + w - (float(w) * (log10f(mh - bin) - log10f(1))) / + (log10f(mh) - log10f(1)); + break; + } + + float value = values[bin]; + + value *= m_gain; + float y = 0.f; + + switch (m_energyScale) { + + case dBScale: + { + float db = thresh; + if (value > 0.f) db = 10.f * log10f(value); + if (db < thresh) db = thresh; + float val = (db - thresh) / -thresh; + y = yorigin - (float(h) * val); + break; + } + + case MeterScale: + y = yorigin - AudioLevel::multiplier_to_preview(value, h); + break; + + default: + y = yorigin - (float(h) * value); + break; + } + + if (m_plotStyle == PlotLines) { + + if (bin == 0) { + path.moveTo(x, y); + } else { + path.lineTo(x, y); + } + + } else if (m_plotStyle == PlotSteps) { + + if (bin == 0) { + path.moveTo(x, y); + } else { + path.lineTo(x, y); + } + path.lineTo(nx, y); + + } else if (m_plotStyle == PlotBlocks) { + + path.moveTo(x, yorigin); + path.lineTo(x, y); + path.lineTo(nx, y); + path.lineTo(nx, yorigin); + path.lineTo(x, yorigin); + } + + py = y; + } + + paint.drawPath(path); + paint.restore(); + +} + +Layer::PropertyList +SliceLayer::getProperties() const +{ + PropertyList list; + list.push_back("Colour"); + list.push_back("Plot Type"); + list.push_back("Sampling Mode"); + list.push_back("Scale"); + list.push_back("Normalize"); + list.push_back("Gain"); + list.push_back("Bin Scale"); + + return list; +} + +QString +SliceLayer::getPropertyLabel(const PropertyName &name) const +{ + if (name == "Colour") return tr("Colour"); + if (name == "Plot Type") return tr("Plot Type"); + if (name == "Energy Scale") return tr("Scale"); + if (name == "Normalize") return tr("Normalize"); + if (name == "Gain") return tr("Gain"); + if (name == "Sampling Mode") return tr("Sampling Mode"); + if (name == "Bin Scale") return tr("Plot X Scale"); + return ""; +} + +Layer::PropertyType +SliceLayer::getPropertyType(const PropertyName &name) const +{ + if (name == "Gain") return RangeProperty; + if (name == "Normalize") return ToggleProperty; + return ValueProperty; +} + +QString +SliceLayer::getPropertyGroupName(const PropertyName &name) const +{ + if (name == "Scale" || + name == "Normalize" || + name == "Sampling Mode" || + name == "Gain") return tr("Scale"); + if (name == "Plot Type" || + name == "Bin Scale") return tr("Plot Type"); + return QString(); +} + +int +SliceLayer::getPropertyRangeAndValue(const PropertyName &name, + int *min, int *max) const +{ + int deft = 0; + + int garbage0, garbage1; + if (!min) min = &garbage0; + if (!max) max = &garbage1; + + if (name == "Gain") { + + *min = -50; + *max = 50; + + std::cerr << "gain is " << m_gain << ", mode is " << m_samplingMode << std::endl; + + deft = lrint(log10(m_gain) * 20.0); + if (deft < *min) deft = *min; + if (deft > *max) deft = *max; + + } else if (name == "Normalize") { + + deft = (m_normalize ? 1 : 0); + + } else if (name == "Colour") { + + *min = 0; + *max = 5; + + if (m_colour == Qt::black) deft = 0; + else if (m_colour == Qt::darkRed) deft = 1; + else if (m_colour == Qt::darkBlue) deft = 2; + else if (m_colour == Qt::darkGreen) deft = 3; + else if (m_colour == QColor(200, 50, 255)) deft = 4; + else if (m_colour == QColor(255, 150, 50)) deft = 5; + + } else if (name == "Scale") { + + *min = 0; + *max = 2; + + deft = (int)m_energyScale; + + } else if (name == "Sampling Mode") { + + *min = 0; + *max = 2; + + deft = (int)m_samplingMode; + + } else if (name == "Plot Type") { + + *min = 0; + *max = 2; + + deft = (int)m_plotStyle; + + } else if (name == "Bin Scale") { + + *min = 0; + *max = 2; + + deft = (int)m_binScale; + + } else { + deft = Layer::getPropertyRangeAndValue(name, min, max); + } + + return deft; +} + +QString +SliceLayer::getPropertyValueLabel(const PropertyName &name, + int value) const +{ + if (name == "Colour") { + switch (value) { + default: + case 0: return tr("Black"); + case 1: return tr("Red"); + case 2: return tr("Blue"); + case 3: return tr("Green"); + case 4: return tr("Purple"); + case 5: return tr("Orange"); + } + } + if (name == "Scale") { + switch (value) { + default: + case 0: return tr("Linear"); + case 1: return tr("Meter"); + case 2: return tr("dB"); + } + } + if (name == "Sampling Mode") { + switch (value) { + default: + case 0: return tr("Any"); + case 1: return tr("Mean"); + case 2: return tr("Peak"); + } + } + if (name == "Plot Type") { + switch (value) { + default: + case 0: return tr("Lines"); + case 1: return tr("Steps"); + case 2: return tr("Blocks"); + } + } + if (name == "Bin Scale") { + switch (value) { + default: + case 0: return tr("Linear"); + case 1: return tr("Log"); + case 2: return tr("Rev Log"); + } + } + return tr("<unknown>"); +} + +RangeMapper * +SliceLayer::getNewPropertyRangeMapper(const PropertyName &name) const +{ + if (name == "Gain") { + return new LinearRangeMapper(-50, 50, -25, 25, tr("dB")); + } + return 0; +} + +void +SliceLayer::setProperty(const PropertyName &name, int value) +{ + if (name == "Gain") { + setGain(pow(10, float(value)/20.0)); + } else if (name == "Colour") { + switch (value) { + default: + case 0: setBaseColour(Qt::black); break; + case 1: setBaseColour(Qt::darkRed); break; + case 2: setBaseColour(Qt::darkBlue); break; + case 3: setBaseColour(Qt::darkGreen); break; + case 4: setBaseColour(QColor(200, 50, 255)); break; + case 5: setBaseColour(QColor(255, 150, 50)); break; + } + } else if (name == "Scale") { + switch (value) { + default: + case 0: setEnergyScale(LinearScale); break; + case 1: setEnergyScale(MeterScale); break; + case 2: setEnergyScale(dBScale); break; + } + } else if (name == "Plot Type") { + setPlotStyle(PlotStyle(value)); + } else if (name == "Sampling Mode") { + switch (value) { + default: + case 0: setSamplingMode(NearestSample); break; + case 1: setSamplingMode(SampleMean); break; + case 2: setSamplingMode(SamplePeak); break; + } + } else if (name == "Bin Scale") { + switch (value) { + default: + case 0: setBinScale(LinearBins); break; + case 1: setBinScale(LogBins); break; + case 2: setBinScale(InvertedLogBins); break; + } + } else if (name == "Normalize") { + setNormalize(value ? true : false); + } +} + +void +SliceLayer::setBaseColour(QColor colour) +{ + if (m_colour == colour) return; + m_colour = colour; + emit layerParametersChanged(); +} + +void +SliceLayer::setEnergyScale(EnergyScale scale) +{ + if (m_energyScale == scale) return; + m_energyScale = scale; + emit layerParametersChanged(); +} + +void +SliceLayer::setSamplingMode(SamplingMode mode) +{ + if (m_samplingMode == mode) return; + m_samplingMode = mode; + emit layerParametersChanged(); +} + +void +SliceLayer::setPlotStyle(PlotStyle style) +{ + if (m_plotStyle == style) return; + m_plotStyle = style; + emit layerParametersChanged(); +} + +void +SliceLayer::setBinScale(BinScale scale) +{ + if (m_binScale == scale) return; + m_binScale = scale; + emit layerParametersChanged(); +} + +void +SliceLayer::setNormalize(bool n) +{ + if (m_normalize == n) return; + m_normalize = n; + emit layerParametersChanged(); +} + +void +SliceLayer::setGain(float gain) +{ + if (m_gain == gain) return; + m_gain = gain; + emit layerParametersChanged(); +} + +QString +SliceLayer::toXmlString(QString indent, QString extraAttributes) const +{ + QString s; + + s += QString("colour=\"%1\" " + "energyScale=\"%2\" " + "samplingMode=\"%3\" " + "gain=\"%4\" " + "normalize=\"%5\"") + .arg(encodeColour(m_colour)) + .arg(m_energyScale) + .arg(m_samplingMode) + .arg(m_gain) + .arg(m_normalize ? "true" : "false"); + + return Layer::toXmlString(indent, extraAttributes + " " + s); +} + +void +SliceLayer::setProperties(const QXmlAttributes &attributes) +{ + bool ok = false; + + QString colourSpec = attributes.value("colour"); + if (colourSpec != "") { + QColor colour(colourSpec); + if (colour.isValid()) { + setBaseColour(QColor(colourSpec)); + } + } + + EnergyScale scale = (EnergyScale) + attributes.value("energyScale").toInt(&ok); + if (ok) setEnergyScale(scale); + + SamplingMode mode = (SamplingMode) + attributes.value("samplingMode").toInt(&ok); + if (ok) setSamplingMode(mode); + + float gain = attributes.value("gain").toFloat(&ok); + if (ok) setGain(gain); + + bool normalize = (attributes.value("normalize").trimmed() == "true"); + setNormalize(normalize); +} + +bool +SliceLayer::getValueExtents(float &min, float &max, bool &logarithmic, + QString &units) const +{ + return false; +} +