annotate layer/Colour3DPlotRenderer.h @ 1127:9fb8dfd7ce4c spectrogram-minor-refactor

Fix threshold in spectrogram -- it wasn't working in the last release. There is a new protocol for this. Formerly the threshold parameter had a range from -50dB to 0 with the default at -50, and -50 treated internally as "no threshold". However, there was a hardcoded, hidden internal threshold for spectrogram colour mapping at -80dB with anything below this being rounded to zero. Now the threshold parameter has range -81 to -1 with the default at -80, -81 is treated internally as "no threshold", and there is no hidden internal threshold. So the default behaviour is the same as before, an effective -80dB threshold, but it is now possible to change this in both directions. Sessions reloaded from prior versions may look slightly different because, if the session says there should be no threshold, there will now actually be no threshold instead of having the hidden internal one. Still need to do something in the UI to make it apparent that the -81dB setting removes the threshold entirely. This is at least no worse than the previous, also obscured, magic -50dB setting.
author Chris Cannam
date Mon, 01 Aug 2016 16:21:01 +0100
parents 50324fca1328
children 998e31e92dbe
rev   line source
Chris@1071 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@1071 2
Chris@1071 3 /*
Chris@1071 4 Sonic Visualiser
Chris@1071 5 An audio file viewer and annotation editor.
Chris@1071 6 Centre for Digital Music, Queen Mary, University of London.
Chris@1071 7 This file copyright 2006-2016 Chris Cannam and QMUL.
Chris@1071 8
Chris@1071 9 This program is free software; you can redistribute it and/or
Chris@1071 10 modify it under the terms of the GNU General Public License as
Chris@1071 11 published by the Free Software Foundation; either version 2 of the
Chris@1071 12 License, or (at your option) any later version. See the file
Chris@1071 13 COPYING included with this distribution for more information.
Chris@1071 14 */
Chris@1071 15
Chris@1071 16 #ifndef COLOUR_3D_PLOT_RENDERER_H
Chris@1071 17 #define COLOUR_3D_PLOT_RENDERER_H
Chris@1071 18
Chris@1071 19 #include "ColourScale.h"
Chris@1073 20 #include "ScrollableImageCache.h"
Chris@1119 21 #include "ScrollableMagRangeCache.h"
Chris@1071 22
Chris@1071 23 #include "base/ColumnOp.h"
Chris@1073 24 #include "base/MagnitudeRange.h"
Chris@1071 25
Chris@1073 26 #include <QRect>
Chris@1073 27 #include <QPainter>
Chris@1073 28 #include <QImage>
Chris@1073 29
Chris@1073 30 class LayerGeometryProvider;
Chris@1082 31 class VerticalBinLayer;
Chris@1071 32 class DenseThreeDimensionalModel;
Chris@1071 33 class Dense3DModelPeakCache;
Chris@1071 34 class FFTModel;
Chris@1071 35
Chris@1103 36 enum class BinDisplay {
Chris@1103 37 AllBins,
Chris@1103 38 PeakBins,
Chris@1103 39 PeakFrequencies
Chris@1103 40 };
Chris@1103 41
Chris@1103 42 enum class BinScale {
Chris@1103 43 Linear,
Chris@1103 44 Log
Chris@1103 45 };
Chris@1103 46
Chris@1071 47 class Colour3DPlotRenderer
Chris@1071 48 {
Chris@1071 49 public:
Chris@1073 50 struct Sources {
Chris@1090 51 Sources() : verticalBinLayer(0), source(0), peaks(0), fft(0) { }
Chris@1073 52
Chris@1073 53 // These must all outlive this class
Chris@1089 54 const VerticalBinLayer *verticalBinLayer; // always
Chris@1100 55 const DenseThreeDimensionalModel *source; // always
Chris@1100 56 const Dense3DModelPeakCache *peaks; // optionally
Chris@1100 57 const FFTModel *fft; // optionally
Chris@1073 58 };
Chris@1073 59
Chris@1071 60 struct Parameters {
Chris@1071 61 Parameters() :
Chris@1071 62 colourScale(ColourScale::Parameters()),
Chris@1104 63 normalization(ColumnNormalization::None),
Chris@1103 64 binDisplay(BinDisplay::AllBins),
Chris@1103 65 binScale(BinScale::Linear),
Chris@1073 66 alwaysOpaque(false),
Chris@1125 67 interpolate(false),
Chris@1112 68 invertVertical(false),
Chris@1125 69 scaleFactor(1.0),
Chris@1112 70 colourRotation(0) { }
Chris@1071 71
Chris@1125 72 /** A complete ColourScale object by value, used for colour
Chris@1125 73 * map conversion. Note that the final display gain setting is
Chris@1125 74 * also encapsulated here. */
Chris@1125 75 ColourScale colourScale;
Chris@1125 76
Chris@1125 77 /** Type of column normalization. */
Chris@1104 78 ColumnNormalization normalization;
Chris@1125 79
Chris@1125 80 /** Selection of bins to display. */
Chris@1071 81 BinDisplay binDisplay;
Chris@1125 82
Chris@1125 83 /** Scale for vertical bin spacing (linear or logarithmic). */
Chris@1071 84 BinScale binScale;
Chris@1125 85
Chris@1125 86 /** Whether cells should always be opaque. If false, then
Chris@1125 87 * large cells (when zoomed in a long way) will be rendered
Chris@1125 88 * translucent in order not to obscure anything in a layer
Chris@1125 89 * beneath. */
Chris@1071 90 bool alwaysOpaque;
Chris@1125 91
Chris@1125 92 /** Whether to apply smoothing when rendering cells at more
Chris@1125 93 * than one pixel per cell. !!! todo: decide about separating
Chris@1125 94 * out x-interpolate and y-interpolate as the spectrogram
Chris@1125 95 * actually does (or used to)
Chris@1125 96 */
Chris@1071 97 bool interpolate;
Chris@1125 98
Chris@1125 99 /** Whether to render the whole caboodle upside-down. */
Chris@1071 100 bool invertVertical;
Chris@1125 101
Chris@1125 102 /** Initial scale factor (e.g. for FFT scaling). This factor
Chris@1125 103 * is applied to all values read from the underlying model
Chris@1125 104 * *before* magnitude ranges are calculated, in contrast to
Chris@1125 105 * the display gain found in the ColourScale parameter. */
Chris@1125 106 double scaleFactor;
Chris@1125 107
Chris@1125 108 /** Colourmap rotation, in the range 0-255. */
Chris@1112 109 int colourRotation;
Chris@1071 110 };
Chris@1073 111
Chris@1073 112 Colour3DPlotRenderer(Sources sources, Parameters parameters) :
Chris@1073 113 m_sources(sources),
Chris@1079 114 m_params(parameters)
Chris@1071 115 { }
Chris@1071 116
Chris@1073 117 struct RenderResult {
Chris@1073 118 /**
Chris@1073 119 * The rect that was actually rendered. May be equal to the
Chris@1073 120 * rect that was requested to render, or may be smaller if
Chris@1073 121 * time ran out and the complete flag was not set.
Chris@1073 122 */
Chris@1073 123 QRect rendered;
Chris@1073 124
Chris@1073 125 /**
Chris@1073 126 * The magnitude range of the data in the rendered area.
Chris@1073 127 */
Chris@1073 128 MagnitudeRange range;
Chris@1073 129 };
Chris@1073 130
Chris@1073 131 /**
Chris@1073 132 * Render the requested area using the given painter, obtaining
Chris@1090 133 * geometry (e.g. start frame) from the given
Chris@1073 134 * LayerGeometryProvider.
Chris@1073 135 *
Chris@1090 136 * The whole of the supplied rect will be rendered and the
Chris@1090 137 * returned QRect will be equal to the supplied QRect. (See
Chris@1090 138 * renderTimeConstrained for an alternative that may render only
Chris@1090 139 * part of the rect in cases where obtaining source data is slow
Chris@1090 140 * and retaining responsiveness is important.)
Chris@1090 141 *
Chris@1090 142 * Note that Colour3DPlotRenderer retains internal cache state
Chris@1090 143 * related to the size and position of the supplied
Chris@1090 144 * LayerGeometryProvider. Although it is valid to call render()
Chris@1090 145 * successively on the same Colour3DPlotRenderer with different
Chris@1090 146 * LayerGeometryProviders, it will be much faster to use a
Chris@1090 147 * dedicated Colour3DPlotRenderer for each LayerGeometryProvider.
Chris@1075 148 *
Chris@1075 149 * If the model to render from is not ready, this will throw a
Chris@1075 150 * std::logic_error exception. The model must be ready and the
Chris@1075 151 * layer requesting the render must not be dormant in its view, so
Chris@1075 152 * that the LayerGeometryProvider returns valid results; it is the
Chris@1075 153 * caller's responsibility to ensure these.
Chris@1073 154 */
Chris@1125 155 RenderResult render(const LayerGeometryProvider *v,
Chris@1125 156 QPainter &paint, QRect rect);
Chris@1076 157
Chris@1076 158 /**
Chris@1076 159 * Render the requested area using the given painter, obtaining
Chris@1076 160 * geometry (e.g. start frame) from the stored
Chris@1076 161 * LayerGeometryProvider.
Chris@1076 162 *
Chris@1076 163 * As much of the rect will be rendered as can be managed given
Chris@1076 164 * internal time constraints (using a RenderTimer object
Chris@1076 165 * internally). The returned QRect (the rendered field in the
Chris@1076 166 * RenderResult struct) will contain the area that was
Chris@1076 167 * rendered. Note that we always render the full requested height,
Chris@1076 168 * it's only width that is time-constrained.
Chris@1076 169 *
Chris@1090 170 * Note that Colour3DPlotRenderer retains internal cache state
Chris@1090 171 * related to the size and position of the supplied
Chris@1090 172 * LayerGeometryProvider. Although it is valid to call render()
Chris@1090 173 * successively on the same Colour3DPlotRenderer with different
Chris@1090 174 * LayerGeometryProviders, it will be much faster to use a
Chris@1090 175 * dedicated Colour3DPlotRenderer for each LayerGeometryProvider.
Chris@1090 176 *
Chris@1076 177 * If the model to render from is not ready, this will throw a
Chris@1076 178 * std::logic_error exception. The model must be ready and the
Chris@1076 179 * layer requesting the render must not be dormant in its view, so
Chris@1076 180 * that the LayerGeometryProvider returns valid results; it is the
Chris@1076 181 * caller's responsibility to ensure these.
Chris@1076 182 */
Chris@1113 183 RenderResult renderTimeConstrained(const LayerGeometryProvider *v,
Chris@1090 184 QPainter &paint, QRect rect);
Chris@1096 185
Chris@1096 186 /**
Chris@1096 187 * Return the area of the largest rectangle within the entire area
Chris@1096 188 * of the cache that is unavailable in the cache. This is only
Chris@1096 189 * valid in relation to a preceding render() call which is
Chris@1096 190 * presumed to have set the area, start frame, and zoom level for
Chris@1096 191 * the cache. It could be used to establish a suitable region for
Chris@1096 192 * a subsequent paint request (because if an area is not in the
Chris@1096 193 * cache, it cannot have been rendered since the cache was
Chris@1096 194 * cleared).
Chris@1096 195 *
Chris@1096 196 * Returns an empty QRect if the cache is entirely valid.
Chris@1096 197 */
Chris@1121 198 QRect getLargestUncachedRect(const LayerGeometryProvider *v);
Chris@1113 199
Chris@1113 200 /**
Chris@1122 201 * Return true if the provider's geometry differs from the cache,
Chris@1122 202 * or if we are not using a cache. i.e. if the cache will be
Chris@1122 203 * regenerated for the next render, or the next render performed
Chris@1122 204 * from scratch.
Chris@1122 205 */
Chris@1122 206 bool geometryChanged(const LayerGeometryProvider *v);
Chris@1122 207
Chris@1122 208 /**
Chris@1113 209 * Return true if the rendering will be opaque. This may be used
Chris@1113 210 * by the calling layer to determine whether it can scroll
Chris@1113 211 * directly without regard to any other layers beneath.
Chris@1113 212 */
Chris@1113 213 bool willRenderOpaque(const LayerGeometryProvider *v) {
Chris@1113 214 return decideRenderType(v) != DirectTranslucent;
Chris@1113 215 }
Chris@1073 216
Chris@1125 217 /**
Chris@1125 218 * Return the colour corresponding to the given value.
Chris@1125 219 * \see ColourScale::getPixel
Chris@1125 220 * \see ColourScale::getColour
Chris@1125 221 */
Chris@1125 222 QColor getColour(double value) const {
Chris@1125 223 return m_params.colourScale.getColour(value, m_params.colourRotation);
Chris@1125 224 }
Chris@1125 225
Chris@1071 226 private:
Chris@1073 227 Sources m_sources;
Chris@1071 228 Parameters m_params;
Chris@1072 229
Chris@1073 230 // Draw buffer is the target of each partial repaint. It is always
Chris@1073 231 // at view height (not model height) and is cleared and repainted
Chris@1073 232 // on each fragment render. The only reason it's stored as a data
Chris@1073 233 // member is to avoid reallocation.
Chris@1073 234 QImage m_drawBuffer;
Chris@1072 235
Chris@1121 236 // A temporary store of magnitude ranges per-column, used when
Chris@1121 237 // rendering to the draw buffer. This always has the same length
Chris@1121 238 // as the width of the draw buffer, and the x coordinates of the
Chris@1121 239 // two containers are equivalent.
Chris@1121 240 std::vector<MagnitudeRange> m_magRanges;
Chris@1121 241
Chris@1119 242 // The image cache is our persistent record of the visible
Chris@1119 243 // area. It is always the same size as the view (i.e. the paint
Chris@1119 244 // size reported by the LayerGeometryProvider) and is scrolled and
Chris@1119 245 // partially repainted internally as appropriate. A render request
Chris@1119 246 // is carried out by repainting to cache (via the draw buffer) any
Chris@1073 247 // area that is being requested but is not valid in the cache, and
Chris@1073 248 // then repainting from cache to the requested painter.
Chris@1073 249 ScrollableImageCache m_cache;
Chris@1073 250
Chris@1119 251 // The mag range cache is our record of the column magnitude
Chris@1119 252 // ranges for each of the columns in the cache. It always has the
Chris@1119 253 // same start frame and width as the image cache, and the column
Chris@1119 254 // indices match up across both. Our cache update mechanism
Chris@1119 255 // guarantees that every valid column in the image cache has a
Chris@1119 256 // valid range in the magnitude cache, but not necessarily vice
Chris@1119 257 // versa (as the image cache is limited to contiguous ranges).
Chris@1119 258 ScrollableMagRangeCache m_magCache;
Chris@1119 259
Chris@1113 260 RenderResult render(const LayerGeometryProvider *v,
Chris@1090 261 QPainter &paint, QRect rect, bool timeConstrained);
Chris@1109 262
Chris@1121 263 MagnitudeRange renderDirectTranslucent(const LayerGeometryProvider *v,
Chris@1121 264 QPainter &paint, QRect rect);
Chris@1109 265
Chris@1113 266 void renderToCachePixelResolution(const LayerGeometryProvider *v, int x0,
Chris@1094 267 int repaintWidth, bool rightToLeft,
Chris@1094 268 bool timeConstrained);
Chris@1109 269
Chris@1113 270 void renderToCacheBinResolution(const LayerGeometryProvider *v, int x0,
Chris@1094 271 int repaintWidth);
Chris@1097 272
Chris@1083 273 int renderDrawBuffer(int w, int h,
Chris@1083 274 const std::vector<int> &binforx,
Chris@1083 275 const std::vector<double> &binfory,
Chris@1083 276 bool usePeaksCache,
Chris@1083 277 bool rightToLeft,
Chris@1083 278 bool timeConstrained);
Chris@1097 279
Chris@1113 280 int renderDrawBufferPeakFrequencies(const LayerGeometryProvider *v,
Chris@1097 281 int w, int h,
Chris@1097 282 const std::vector<int> &binforx,
Chris@1097 283 const std::vector<double> &binfory,
Chris@1097 284 bool rightToLeft,
Chris@1097 285 bool timeConstrained);
Chris@1097 286
Chris@1095 287 void recreateDrawBuffer(int w, int h);
Chris@1079 288 void clearDrawBuffer(int w, int h);
Chris@1109 289
Chris@1109 290 enum RenderType {
Chris@1109 291 DrawBufferPixelResolution,
Chris@1109 292 DrawBufferBinResolution,
Chris@1109 293 DirectTranslucent
Chris@1109 294 };
Chris@1109 295
Chris@1113 296 RenderType decideRenderType(const LayerGeometryProvider *) const;
Chris@1071 297 };
Chris@1071 298
Chris@1071 299 #endif
Chris@1071 300