Mercurial > hg > svgui
changeset 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 | fcc043f75c41 |
children | d13e209bfa94 |
files | layer/Colour3DPlotLayer.cpp layer/Colour3DPlotLayer.h layer/LayerFactory.cpp layer/LayerFactory.h layer/SliceLayer.cpp layer/SliceLayer.h layer/SliceableLayer.h layer/SpectrogramLayer.cpp layer/SpectrogramLayer.h layer/SpectrumLayer.cpp layer/SpectrumLayer.h layer/layer.pro widgets/Panner.cpp widgets/PropertyBox.cpp |
diffstat | 14 files changed, 899 insertions(+), 365 deletions(-) [+] |
line wrap: on
line diff
--- a/layer/Colour3DPlotLayer.cpp Wed Jan 24 17:14:24 2007 +0000 +++ b/layer/Colour3DPlotLayer.cpp Fri Jan 26 16:59:57 2007 +0000 @@ -30,7 +30,6 @@ Colour3DPlotLayer::Colour3DPlotLayer() : - Layer(), m_model(0), m_cache(0), m_colourScale(LinearScale) @@ -45,6 +44,8 @@ void Colour3DPlotLayer::setModel(const DenseThreeDimensionalModel *model) { + if (m_model == model) return; + const DenseThreeDimensionalModel *oldModel = m_model; m_model = model; if (!m_model || !m_model->isOK()) return; @@ -60,6 +61,7 @@ this, SLOT(cacheInvalid(size_t, size_t))); emit modelReplaced(); + emit sliceableModelReplaced(oldModel, model); } void
--- a/layer/Colour3DPlotLayer.h Wed Jan 24 17:14:24 2007 +0000 +++ b/layer/Colour3DPlotLayer.h Fri Jan 26 16:59:57 2007 +0000 @@ -16,7 +16,7 @@ #ifndef _COLOUR_3D_PLOT_H_ #define _COLOUR_3D_PLOT_H_ -#include "Layer.h" +#include "SliceableLayer.h" #include "data/model/DenseThreeDimensionalModel.h" @@ -37,7 +37,7 @@ * ever actually used. */ -class Colour3DPlotLayer : public Layer +class Colour3DPlotLayer : public SliceableLayer { Q_OBJECT @@ -88,6 +88,8 @@ void setColourScale(ColourScale); ColourScale getColourScale() const { return m_colourScale; } + virtual const Model *getSliceableModel() const { return m_model; } + protected slots: void cacheInvalid(); void cacheInvalid(size_t startFrame, size_t endFrame);
--- a/layer/LayerFactory.cpp Wed Jan 24 17:14:24 2007 +0000 +++ b/layer/LayerFactory.cpp Fri Jan 26 16:59:57 2007 +0000 @@ -24,6 +24,8 @@ #include "TextLayer.h" #include "Colour3DPlotLayer.h" #include "SpectrumLayer.h" +#include "SliceLayer.h" +#include "SliceableLayer.h" #include "data/model/RangeSummarisableTimeValueModel.h" #include "data/model/DenseTimeValueModel.h" @@ -61,6 +63,7 @@ case Text: return Layer::tr("Text"); case Colour3DPlot: return Layer::tr("Colour 3D Plot"); case Spectrum: return Layer::tr("Spectrum"); + case Slice: return Layer::tr("Time Slice"); case MelodicRangeSpectrogram: // The user can change all the parameters of this after the @@ -78,6 +81,39 @@ return Layer::tr("Layer"); } +bool +LayerFactory::isLayerSliceable(const Layer *layer) +{ + if (dynamic_cast<const SliceableLayer *>(layer)) { + if (dynamic_cast<const SpectrogramLayer *>(layer)) { + + //!!! We can create slices of spectrograms, but there's a + // problem managing the models. The source model for the + // slice layer has to be one of the spectrogram's FFT + // models -- that's fine, except that we can't store & + // recall the slice layer with a reference to that model + // because the model is internal to the spectrogram layer + // and the document has no record of it. We would need + // some other way of managing models that are used in this + // way. For the moment we just don't allow slices of + // spectrograms -- and provide a spectrum layer for this + // instead. + // + // This business needs a bit more thought -- either come + // up with a sensible way to deal with that stuff, or + // simplify the existing slice layer logic so that it + // doesn't have to deal with models disappearing on it at + // all (and use the normal Document setModel mechanism to + // set its sliceable model instead of the fancy pants + // nonsense it's doing at the moment). + + return false; + } + return true; + } + return false; +} + LayerFactory::LayerTypeSet LayerFactory::getValidLayerTypes(Model *model) { @@ -85,6 +121,11 @@ if (dynamic_cast<DenseThreeDimensionalModel *>(model)) { types.insert(Colour3DPlot); + types.insert(Slice); + } + + if (dynamic_cast<RangeSummarisableTimeValueModel *>(model)) { + types.insert(Waveform); } if (dynamic_cast<DenseTimeValueModel *>(model)) { @@ -93,10 +134,6 @@ types.insert(PeakFrequencySpectrogram); } - if (dynamic_cast<RangeSummarisableTimeValueModel *>(model)) { - types.insert(Waveform); - } - if (dynamic_cast<SparseOneDimensionalModel *>(model)) { types.insert(TimeInstants); } @@ -147,6 +184,7 @@ if (dynamic_cast<const TextLayer *>(layer)) return Text; if (dynamic_cast<const Colour3DPlotLayer *>(layer)) return Colour3DPlot; if (dynamic_cast<const SpectrumLayer *>(layer)) return Spectrum; + if (dynamic_cast<const SliceLayer *>(layer)) return Slice; return UnknownLayer; } @@ -163,6 +201,7 @@ case Text: return "text"; case Colour3DPlot: return "colour3d"; case Spectrum: return "spectrum"; + case Slice: return "spectrum"; default: return "unknown"; } } @@ -180,6 +219,7 @@ case Text: return "text"; case Colour3DPlot: return "colour3dplot"; case Spectrum: return "spectrum"; + case Slice: return "slice"; default: return "unknown"; } } @@ -196,6 +236,7 @@ if (name == "text") return Text; if (name == "colour3dplot") return Colour3DPlot; if (name == "spectrum") return Spectrum; + if (name == "slice") return Slice; return UnknownLayer; } @@ -237,6 +278,9 @@ if (trySetModel<SpectrumLayer, DenseTimeValueModel>(layer, model)) return; + +// if (trySetModel<SliceLayer, DenseThreeDimensionalModel>(layer, model)) +// return; } Model * @@ -325,6 +369,10 @@ layer = new SpectrumLayer; break; + case Slice: + layer = new SliceLayer; + break; + case MelodicRangeSpectrogram: layer = new SpectrogramLayer(SpectrogramLayer::MelodicRange); break;
--- a/layer/LayerFactory.h Wed Jan 24 17:14:24 2007 +0000 +++ b/layer/LayerFactory.h Fri Jan 26 16:59:57 2007 +0000 @@ -37,6 +37,7 @@ Text, Colour3DPlot, Spectrum, + Slice, // Layers with different initial parameters MelodicRangeSpectrogram, @@ -60,6 +61,8 @@ QString getLayerPresentationName(LayerType type); + bool isLayerSliceable(const Layer *); + void setModel(Layer *layer, Model *model); Model *createEmptyModel(LayerType type, Model *baseModel);
--- /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; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/layer/SliceLayer.h Fri Jan 26 16:59:57 2007 +0000 @@ -0,0 +1,109 @@ + +/* -*- 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. +*/ + +#ifndef _SLICE_LAYER_H_ +#define _SLICE_LAYER_H_ + +#include "Layer.h" + +#include "base/Window.h" + +#include "data/model/DenseThreeDimensionalModel.h" + +#include <QColor> + +class SliceLayer : public Layer +{ + Q_OBJECT + +public: + SliceLayer(); + ~SliceLayer(); + +// virtual void setModel(const Model *model); +// virtual const Model *getModel() const { return m_model; } + virtual const Model *getModel() const { return 0; } + + void setSliceableModel(const Model *model); + + virtual void paint(View *v, QPainter &paint, QRect rect) const; + + virtual PropertyList getProperties() const; + virtual QString getPropertyLabel(const PropertyName &) const; + virtual PropertyType getPropertyType(const PropertyName &) const; + virtual QString getPropertyGroupName(const PropertyName &) const; + virtual int getPropertyRangeAndValue(const PropertyName &, + int *min, int *max) const; + virtual QString getPropertyValueLabel(const PropertyName &, + int value) const; + virtual RangeMapper *getNewPropertyRangeMapper(const PropertyName &) const; + virtual void setProperty(const PropertyName &, int value); + virtual void setProperties(const QXmlAttributes &); + + virtual bool getValueExtents(float &min, float &max, + bool &logarithmic, QString &unit) const; + + virtual bool isLayerScrollable(const View *v) const { return false; } + + enum EnergyScale { LinearScale, MeterScale, dBScale }; + + enum SamplingMode { NearestSample, SampleMean, SamplePeak }; + + enum PlotStyle { PlotLines, PlotSteps, PlotBlocks }; + + enum BinScale { LinearBins, LogBins, InvertedLogBins }; + + void setBaseColour(QColor); + QColor getBaseColour() const { return m_colour; } + + void setEnergyScale(EnergyScale); + EnergyScale getEnergyScale() const { return m_energyScale; } + + void setSamplingMode(SamplingMode); + SamplingMode getSamplingMode() const { return m_samplingMode; } + + void setPlotStyle(PlotStyle style); + PlotStyle getPlotStyle() const { return m_plotStyle; } + + void setBinScale(BinScale scale); + BinScale getBinScale() const { return m_binScale; } + + void setGain(float gain); + float getGain() const; + + void setNormalize(bool n); + bool getNormalize() const; + + virtual QString toXmlString(QString indent = "", + QString extraAttributes = "") const; + +public slots: + void sliceableModelReplaced(const Model *, const Model *); + void modelAboutToBeDeleted(Model *); + +protected: + const DenseThreeDimensionalModel *m_sliceableModel; + QColor m_colour; + EnergyScale m_energyScale; + SamplingMode m_samplingMode; + PlotStyle m_plotStyle; + BinScale m_binScale; + bool m_normalize; + bool m_bias; + float m_gain; +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/layer/SliceableLayer.h Fri Jan 26 16:59:57 2007 +0000 @@ -0,0 +1,50 @@ +/* -*- 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 2007 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. +*/ + +#ifndef _SLICEABLE_LAYER_H_ +#define _SLICEABLE_LAYER_H_ + +#include "Layer.h" + +/** + * Base class for layers that can be sliced, that is, that contain + * models appropriate for use in a SliceLayer. + */ + +class SliceableLayer : public Layer +{ + Q_OBJECT + +public: + // Get a model that can be sliced, i.e. a + // DenseThreeDimensionalModel. This may be the layer's usual + // model, or it may be a model derived from it (e.g. FFTModel in a + // spectrogram that was constructed from a DenseTimeValueModel). + // The SliceableLayer retains ownership of the model, and will + // emit sliceableModelReplaced if it is about to become invalid. + virtual const Model *getSliceableModel() const = 0; + +signals: + // Emitted when a model that was obtained through + // getSliceableModel is about to be deleted. If replacement is + // non-NULL, it may be used instead. + void sliceableModelReplaced(const Model *modelToBeReplaced, + const Model *replacement); +}; + +#endif + + +
--- a/layer/SpectrogramLayer.cpp Wed Jan 24 17:14:24 2007 +0000 +++ b/layer/SpectrogramLayer.cpp Fri Jan 26 16:59:57 2007 +0000 @@ -39,7 +39,6 @@ //#define DEBUG_SPECTROGRAM_REPAINT 1 SpectrogramLayer::SpectrogramLayer(Configuration config) : - Layer(), m_model(0), m_channel(0), m_windowSize(1024), @@ -62,7 +61,8 @@ m_lastEmittedZoomStep(-1), m_updateTimer(0), m_candidateFillStartFrame(0), - m_exiting(false) + m_exiting(false), + m_sliceableModel(0) { if (config == MelodicRange) { setWindowSize(8192); @@ -901,6 +901,20 @@ m_pixmapCaches.erase(v); if (m_fftModels.find(v) != m_fftModels.end()) { + + if (m_sliceableModel == m_fftModels[v].first) { + bool replaced = false; + for (ViewFFTMap::iterator i = m_fftModels.begin(); + i != m_fftModels.end(); ++i) { + if (i->second.first != m_sliceableModel) { + emit sliceableModelReplaced(m_sliceableModel, i->second.first); + replaced = true; + break; + } + } + if (!replaced) emit sliceableModelReplaced(m_sliceableModel, 0); + } + delete m_fftModels[v].first; m_fftModels.erase(v); } @@ -1626,6 +1640,12 @@ return 0; } + if (!m_sliceableModel) { + std::cerr << "SpectrogramLayer: emitting sliceableModelReplaced(0, " << model << ")" << std::endl; + ((SpectrogramLayer *)this)->sliceableModelReplaced(0, model); + m_sliceableModel = model; + } + m_fftModels[v] = FFTFillPair(model, 0); model->resume(); @@ -1640,6 +1660,15 @@ return m_fftModels[v].first; } +const Model * +SpectrogramLayer::getSliceableModel() const +{ + if (m_sliceableModel) return m_sliceableModel; + if (m_fftModels.empty()) return 0; + m_sliceableModel = m_fftModels.begin()->second.first; + return m_sliceableModel; +} + void SpectrogramLayer::invalidateFFTModels() { @@ -1649,6 +1678,12 @@ } m_fftModels.clear(); + + if (m_sliceableModel) { + std::cerr << "SpectrogramLayer: emitting sliceableModelReplaced(" << m_sliceableModel << ", 0)" << std::endl; + emit sliceableModelReplaced(m_sliceableModel, 0); + m_sliceableModel = 0; + } } void
--- a/layer/SpectrogramLayer.h Wed Jan 24 17:14:24 2007 +0000 +++ b/layer/SpectrogramLayer.h Fri Jan 26 16:59:57 2007 +0000 @@ -16,7 +16,7 @@ #ifndef _SPECTROGRAM_LAYER_H_ #define _SPECTROGRAM_LAYER_H_ -#include "Layer.h" +#include "SliceableLayer.h" #include "base/Window.h" #include "base/RealTime.h" #include "base/Thread.h" @@ -43,7 +43,7 @@ * DenseTimeValueModel) in spectrogram form. */ -class SpectrogramLayer : public Layer, +class SpectrogramLayer : public SliceableLayer, public PowerOfSqrtTwoZoomConstraint { Q_OBJECT @@ -215,6 +215,8 @@ virtual void setVerticalZoomStep(int); virtual RangeMapper *getNewVerticalZoomRangeMapper() const; + virtual const Model *getSliceableModel() const; + protected slots: void cacheInvalid(); void cacheInvalid(size_t startFrame, size_t endFrame); @@ -337,6 +339,7 @@ typedef std::map<const View *, FFTFillPair> ViewFFTMap; typedef std::vector<float> FloatVector; mutable ViewFFTMap m_fftModels; + mutable Model *m_sliceableModel; class MagnitudeRange { public:
--- a/layer/SpectrumLayer.cpp Wed Jan 24 17:14:24 2007 +0000 +++ b/layer/SpectrumLayer.cpp Fri Jan 26 16:59:57 2007 +0000 @@ -22,18 +22,10 @@ #include "base/Preferences.h" #include "base/RangeMapper.h" -#include <QPainter> -#include <QPainterPath> - SpectrumLayer::SpectrumLayer() : - m_model(0), - m_channelMode(MixChannels), + m_originModel(0), m_channel(-1), m_channelSet(false), - m_colour(Qt::darkBlue), - m_energyScale(dBScale), - m_normalize(false), - m_gain(1.0), m_windowSize(1024), m_windowType(HanningWindow), m_windowHopLevel(2) @@ -46,45 +38,40 @@ SpectrumLayer::~SpectrumLayer() { - for (size_t i = 0; i < m_fft.size(); ++i) delete m_fft[i]; + //!!! delete parent's model +// for (size_t i = 0; i < m_fft.size(); ++i) delete m_fft[i]; } void SpectrumLayer::setModel(DenseTimeValueModel *model) { - m_model = model; - setupFFTs(); + if (m_originModel == model) return; + m_originModel = model; + setupFFT(); } void -SpectrumLayer::setupFFTs() +SpectrumLayer::setupFFT() { - for (size_t i = 0; i < m_fft.size(); ++i) delete m_fft[i]; - m_fft.clear(); - - int minChannel = m_channel, maxChannel = m_channel; + FFTModel *oldFFT = dynamic_cast<FFTModel *> + (const_cast<DenseThreeDimensionalModel *>(m_sliceableModel)); - if (m_channel == -1 && - m_channelMode != MixChannels) { - minChannel = 0; - maxChannel = 0; - if (m_model->getChannelCount() > 1) { - maxChannel = m_model->getChannelCount() - 1; - } + if (oldFFT) { + setSliceableModel(0); + delete oldFFT; } - for (int c = minChannel; c <= maxChannel; ++c) { - - m_fft.push_back(new FFTModel(m_model, - c, - HanningWindow, - m_windowSize, - getWindowIncrement(), - m_windowSize, - true)); + FFTModel *newFFT = new FFTModel(m_originModel, + m_channel, + m_windowType, + m_windowSize, + getWindowIncrement(), + m_windowSize, + true); - if (m_channelSet) m_fft[m_fft.size()-1]->resume(); - } + setSliceableModel(newFFT); + + newFFT->resume(); } void @@ -92,133 +79,44 @@ { m_channelSet = true; + FFTModel *fft = dynamic_cast<FFTModel *> + (const_cast<DenseThreeDimensionalModel *>(m_sliceableModel)); + if (m_channel == channel) { - for (size_t i = 0; i < m_fft.size(); ++i) { - m_fft[i]->resume(); - } + if (fft) fft->resume(); return; } m_channel = channel; - if (!m_fft.empty()) setupFFTs(); + if (!fft) setupFFT(); emit layerParametersChanged(); } -void -SpectrumLayer::paint(View *v, QPainter &paint, QRect rect) const -{ - if (m_fft.empty()) return; - if (!m_channelSet) { - for (size_t i = 0; i < m_fft.size(); ++i) { - m_fft[i]->resume(); - } - } - - FFTModel *fft = m_fft[0]; //!!! for now - - int windowIncrement = getWindowIncrement(); - - size_t f = v->getCentreFrame(); - - 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); - - size_t column = f / windowIncrement; - - paint.save(); - paint.setPen(m_colour); - paint.setRenderHint(QPainter::Antialiasing, false); - - QPainterPath path; - float thresh = -80.f; - - for (size_t bin = 0; bin < fft->getHeight(); ++bin) { - - float x = xorigin + (float(w) * bin) / fft->getHeight(); - float mag; - if (m_normalize) { - mag = fft->getNormalizedMagnitudeAt(column, bin); - } else { - mag = fft->getMagnitudeAt(column, bin); - } - mag *= m_gain; - float y = 0.f; - - switch (m_energyScale) { - - case dBScale: - { - float db = thresh; - if (mag > 0.f) db = 10.f * log10f(mag); - if (db < thresh) db = thresh; - float val = (db - thresh) / -thresh; - y = yorigin - (float(h) * val); - break; - } - - case MeterScale: - y = yorigin - AudioLevel::multiplier_to_preview(mag, h); - break; - - default: - y = yorigin - (float(h) * mag); - break; - } - - if (bin == 0) { - path.moveTo(x, y); - } else { - path.lineTo(x, y); - } - } - - paint.drawPath(path); - paint.restore(); - -} - Layer::PropertyList SpectrumLayer::getProperties() const { - PropertyList list; - list.push_back("Colour"); - list.push_back("Scale"); - list.push_back("Normalize"); - list.push_back("Gain"); + PropertyList list = SliceLayer::getProperties(); list.push_back("Window Size"); list.push_back("Window Increment"); - - if (m_model && m_model->getChannelCount() > 1 && m_channel == -1) { - list.push_back("Channels"); - } - return list; } QString SpectrumLayer::getPropertyLabel(const PropertyName &name) const { - if (name == "Colour") return tr("Colour"); - if (name == "Energy Scale") return tr("Scale"); - if (name == "Channels") return tr("Channels"); if (name == "Window Size") return tr("Window Size"); if (name == "Window Increment") return tr("Window Overlap"); - if (name == "Normalize") return tr("Normalize"); - if (name == "Gain") return tr("Gain"); - return ""; + return SliceLayer::getPropertyLabel(name); } Layer::PropertyType SpectrumLayer::getPropertyType(const PropertyName &name) const { - if (name == "Gain") return RangeProperty; - if (name == "Normalize") return ToggleProperty; - return ValueProperty; + if (name == "Window Size") return ValueProperty; + if (name == "Window Increment") return ValueProperty; + return SliceLayer::getPropertyType(name); } QString @@ -226,10 +124,7 @@ { if (name == "Window Size" || name == "Window Increment") return tr("Window"); - if (name == "Scale" || - name == "Normalize" || - name == "Gain") return tr("Energy Scale"); - return QString(); + return SliceLayer::getPropertyGroupName(name); } int @@ -242,47 +137,7 @@ if (!min) min = &garbage0; if (!max) max = &garbage1; - if (name == "Gain") { - - *min = -50; - *max = 50; - - 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 == "Channels") { - - *min = 0; - *max = 2; - if (m_channelMode == MixChannels) deft = 1; - else if (m_channelMode == OverlayChannels) deft = 2; - else deft = 0; - - } else if (name == "Scale") { - - *min = 0; - *max = 2; - - deft = (int)m_energyScale; - - } else if (name == "Window Size") { + if (name == "Window Size") { *min = 0; *max = 10; @@ -299,7 +154,8 @@ deft = m_windowHopLevel; } else { - deft = Layer::getPropertyRangeAndValue(name, min, max); + + deft = SliceLayer::getPropertyRangeAndValue(name, min, max); } return deft; @@ -309,33 +165,6 @@ SpectrumLayer::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 == "Channels") { - switch (value) { - default: - case 0: return tr("Separate"); - case 1: return tr("Mean"); - case 2: return tr("Overlay"); - } - } if (name == "Window Size") { return QString("%1").arg(32 << value); } @@ -350,83 +179,33 @@ case 5: return tr("93.75 %"); } } - return tr("<unknown>"); + return SliceLayer::getPropertyValueLabel(name, value); } RangeMapper * SpectrumLayer::getNewPropertyRangeMapper(const PropertyName &name) const { - if (name == "Gain") { - return new LinearRangeMapper(-50, 50, -25, 25, tr("dB")); - } - return 0; + return SliceLayer::getNewPropertyRangeMapper(name); } void SpectrumLayer::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 == "Channels") { - if (value == 1) setChannelMode(MixChannels); - else if (value == 2) setChannelMode(OverlayChannels); - else setChannelMode(SeparateChannels); - } 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 == "Window Size") { + if (name == "Window Size") { setWindowSize(32 << value); } else if (name == "Window Increment") { setWindowHopLevel(value); - } else if (name == "Normalize") { - setNormalize(value ? true : false); + } else { + SliceLayer::setProperty(name, value); } } void -SpectrumLayer::setBaseColour(QColor colour) -{ - if (m_colour == colour) return; - m_colour = colour; - emit layerParametersChanged(); -} - -void -SpectrumLayer::setChannelMode(ChannelMode channelMode) -{ - if (m_channelMode == channelMode) return; - m_channelMode = channelMode; - emit layerParametersChanged(); -} - -void -SpectrumLayer::setEnergyScale(EnergyScale scale) -{ - if (m_energyScale == scale) return; - m_energyScale = scale; - emit layerParametersChanged(); -} - -void SpectrumLayer::setWindowSize(size_t ws) { if (m_windowSize == ws) return; m_windowSize = ws; - setupFFTs(); + setupFFT(); emit layerParametersChanged(); } @@ -435,7 +214,7 @@ { if (m_windowHopLevel == v) return; m_windowHopLevel = v; - setupFFTs(); + setupFFT(); emit layerParametersChanged(); } @@ -444,23 +223,7 @@ { if (m_windowType == w) return; m_windowType = w; - setupFFTs(); - emit layerParametersChanged(); -} - -void -SpectrumLayer::setNormalize(bool n) -{ - if (m_normalize == n) return; - m_normalize = n; - emit layerParametersChanged(); -} - -void -SpectrumLayer::setGain(float gain) -{ - if (m_gain == gain) return; - m_gain = gain; + setupFFT(); emit layerParametersChanged(); } @@ -478,24 +241,12 @@ { QString s; - s += QString("colour=\"%1\" " - "channelMode=\"%2\" " - "channel=\"%3\" " - "energyScale=\"%4\" " - "windowSize=\"%5\" " - "windowHopLevel=\"%6\" " - "gain=\"%7\" " - "normalize=\"%8\"") - .arg(encodeColour(m_colour)) - .arg(m_channelMode) - .arg(m_channel) - .arg(m_energyScale) + s += QString("windowSize=\"%1\" " + "windowHopLevel=\"%2\"") .arg(m_windowSize) - .arg(m_windowHopLevel) - .arg(m_gain) - .arg(m_normalize ? "true" : "false"); + .arg(m_windowHopLevel); - return Layer::toXmlString(indent, extraAttributes + " " + s); + return SliceLayer::toXmlString(indent, extraAttributes + " " + s); } void @@ -503,36 +254,11 @@ { bool ok = false; - QString colourSpec = attributes.value("colour"); - if (colourSpec != "") { - QColor colour(colourSpec); - if (colour.isValid()) { - setBaseColour(QColor(colourSpec)); - } - } - - ChannelMode channelMode = (ChannelMode) - attributes.value("channelMode").toInt(&ok); - if (ok) setChannelMode(channelMode); - - int channel = attributes.value("channel").toInt(&ok); - if (ok) setChannel(channel); - - EnergyScale scale = (EnergyScale) - attributes.value("energyScale").toInt(&ok); - if (ok) setEnergyScale(scale); - size_t windowSize = attributes.value("windowSize").toUInt(&ok); if (ok) setWindowSize(windowSize); size_t windowHopLevel = attributes.value("windowHopLevel").toUInt(&ok); if (ok) setWindowHopLevel(windowHopLevel); - - float gain = attributes.value("gain").toFloat(&ok); - if (ok) setGain(gain); - - bool normalize = (attributes.value("normalize").trimmed() == "true"); - setNormalize(normalize); } bool
--- a/layer/SpectrumLayer.h Wed Jan 24 17:14:24 2007 +0000 +++ b/layer/SpectrumLayer.h Fri Jan 26 16:59:57 2007 +0000 @@ -17,7 +17,7 @@ #ifndef _SPECTRUM_LAYER_H_ #define _SPECTRUM_LAYER_H_ -#include "Layer.h" +#include "SliceLayer.h" #include "base/Window.h" @@ -27,7 +27,7 @@ class FFTModel; -class SpectrumLayer : public Layer +class SpectrumLayer : public SliceLayer { Q_OBJECT @@ -36,8 +36,7 @@ ~SpectrumLayer(); void setModel(DenseTimeValueModel *model); - virtual const Model *getModel() const { return m_model; } - virtual void paint(View *v, QPainter &paint, QRect rect) const; + virtual const Model *getModel() const { return m_originModel; } virtual PropertyList getProperties() const; virtual QString getPropertyLabel(const PropertyName &) const; @@ -56,22 +55,9 @@ virtual bool isLayerScrollable(const View *v) const { return false; } - enum ChannelMode { SeparateChannels, MixChannels, OverlayChannels }; - - void setChannelMode(ChannelMode); - ChannelMode getChannelCount() const { return m_channelMode; } - void setChannel(int); int getChannel() const { return m_channel; } - enum EnergyScale { LinearScale, MeterScale, dBScale }; - - void setBaseColour(QColor); - QColor getBaseColour() const { return m_colour; } - - void setEnergyScale(EnergyScale); - EnergyScale getEnergyScale() const { return m_energyScale; } - void setWindowSize(size_t); size_t getWindowSize() const { return m_windowSize; } @@ -81,12 +67,6 @@ void setWindowType(WindowType type); WindowType getWindowType() const { return m_windowType; } - void setGain(float gain); - float getGain() const; - - void setNormalize(bool n); - bool getNormalize() const; - virtual QString toXmlString(QString indent = "", QString extraAttributes = "") const; @@ -94,20 +74,19 @@ void preferenceChanged(PropertyContainer::PropertyName name); protected: - DenseTimeValueModel *m_model; - std::vector<FFTModel *> m_fft; - ChannelMode m_channelMode; + // make this SliceLayer method unavailable to the general public +// virtual void setModel(DenseThreeDimensionalModel *model) { +// SliceLayer::setModel(model); +// } + + DenseTimeValueModel *m_originModel; int m_channel; bool m_channelSet; - QColor m_colour; - EnergyScale m_energyScale; - bool m_normalize; - float m_gain; size_t m_windowSize; WindowType m_windowType; size_t m_windowHopLevel; - void setupFFTs(); + void setupFFT(); size_t getWindowIncrement() const { if (m_windowHopLevel == 0) return m_windowSize;
--- a/layer/layer.pro Wed Jan 24 17:14:24 2007 +0000 +++ b/layer/layer.pro Fri Jan 26 16:59:57 2007 +0000 @@ -20,6 +20,8 @@ NoteLayer.h \ SpectrogramLayer.h \ SpectrumLayer.h \ + SliceLayer.h \ + SliceableLayer.h \ TextLayer.h \ TimeInstantLayer.h \ TimeRulerLayer.h \ @@ -31,6 +33,7 @@ NoteLayer.cpp \ SpectrogramLayer.cpp \ SpectrumLayer.cpp \ + SliceLayer.cpp \ TextLayer.cpp \ TimeInstantLayer.cpp \ TimeRulerLayer.cpp \
--- a/widgets/Panner.cpp Wed Jan 24 17:14:24 2007 +0000 +++ b/widgets/Panner.cpp Fri Jan 26 16:59:57 2007 +0000 @@ -104,7 +104,7 @@ void Panner::wheelEvent(QWheelEvent *e) { - if (e->delta() > 0) { + if (e->delta() < 0) { m_rectY += 0.1; } else { m_rectY -= 0.1;
--- a/widgets/PropertyBox.cpp Wed Jan 24 17:14:24 2007 +0000 +++ b/widgets/PropertyBox.cpp Fri Jan 26 16:59:57 2007 +0000 @@ -118,6 +118,8 @@ Layer *layer = dynamic_cast<Layer *>(m_container); if (layer) { + disconnect(layer, SIGNAL(modelReplaced()), + this, SLOT(populateViewPlayFrame())); connect(layer, SIGNAL(modelReplaced()), this, SLOT(populateViewPlayFrame())); }