annotate base/RangeMapper.cpp @ 1199:4d0d94ba2ea7 spectrogram-minor-refactor

Re-enable (little-used) normalise-visible-range option in spectrogram layer
author Chris Cannam
date Wed, 03 Aug 2016 16:16:23 +0100
parents c7b9c902642f
children 6f7a440b6218
rev   line source
Chris@189 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@189 2
Chris@189 3 /*
Chris@189 4 Sonic Visualiser
Chris@189 5 An audio file viewer and annotation editor.
Chris@189 6 Centre for Digital Music, Queen Mary, University of London.
Chris@202 7 This file copyright 2006 QMUL.
Chris@189 8
Chris@189 9 This program is free software; you can redistribute it and/or
Chris@189 10 modify it under the terms of the GNU General Public License as
Chris@189 11 published by the Free Software Foundation; either version 2 of the
Chris@189 12 License, or (at your option) any later version. See the file
Chris@189 13 COPYING included with this distribution for more information.
Chris@189 14 */
Chris@189 15
Chris@189 16 #include "RangeMapper.h"
Chris@573 17 #include "system/System.h"
Chris@189 18
Chris@189 19 #include <cassert>
Chris@189 20 #include <cmath>
Chris@189 21
Chris@189 22 #include <iostream>
Chris@189 23
Chris@189 24 LinearRangeMapper::LinearRangeMapper(int minpos, int maxpos,
Chris@1038 25 double minval, double maxval,
Chris@464 26 QString unit, bool inverted) :
Chris@189 27 m_minpos(minpos),
Chris@189 28 m_maxpos(maxpos),
Chris@189 29 m_minval(minval),
Chris@189 30 m_maxval(maxval),
Chris@464 31 m_unit(unit),
Chris@464 32 m_inverted(inverted)
Chris@189 33 {
Chris@189 34 assert(m_maxval != m_minval);
Chris@189 35 assert(m_maxpos != m_minpos);
Chris@189 36 }
Chris@189 37
Chris@189 38 int
Chris@1038 39 LinearRangeMapper::getPositionForValue(double value) const
Chris@189 40 {
Chris@885 41 int position = getPositionForValueUnclamped(value);
Chris@885 42 if (position < m_minpos) position = m_minpos;
Chris@885 43 if (position > m_maxpos) position = m_maxpos;
Chris@885 44 return position;
Chris@885 45 }
Chris@885 46
Chris@885 47 int
Chris@1038 48 LinearRangeMapper::getPositionForValueUnclamped(double value) const
Chris@885 49 {
Chris@190 50 int position = m_minpos +
Chris@1038 51 int(lrint(((value - m_minval) / (m_maxval - m_minval))
Chris@1038 52 * (m_maxpos - m_minpos)));
Chris@879 53 if (m_inverted) return m_maxpos - (position - m_minpos);
Chris@464 54 else return position;
Chris@189 55 }
Chris@189 56
Chris@1038 57 double
Chris@189 58 LinearRangeMapper::getValueForPosition(int position) const
Chris@189 59 {
Chris@879 60 if (position < m_minpos) position = m_minpos;
Chris@879 61 if (position > m_maxpos) position = m_maxpos;
Chris@1038 62 double value = getValueForPositionUnclamped(position);
Chris@885 63 return value;
Chris@885 64 }
Chris@885 65
Chris@1038 66 double
Chris@885 67 LinearRangeMapper::getValueForPositionUnclamped(int position) const
Chris@885 68 {
Chris@885 69 if (m_inverted) position = m_maxpos - (position - m_minpos);
Chris@1038 70 double value = m_minval +
Chris@1038 71 ((double(position - m_minpos) / double(m_maxpos - m_minpos))
Chris@190 72 * (m_maxval - m_minval));
Chris@1196 73 cerr << "getValueForPositionUnclamped(" << position << "): minval " << m_minval << ", maxval " << m_maxval << ", value " << value << endl;
Chris@189 74 return value;
Chris@189 75 }
Chris@189 76
Chris@189 77 LogRangeMapper::LogRangeMapper(int minpos, int maxpos,
Chris@1038 78 double minval, double maxval,
Chris@464 79 QString unit, bool inverted) :
Chris@189 80 m_minpos(minpos),
Chris@189 81 m_maxpos(maxpos),
Chris@464 82 m_unit(unit),
Chris@464 83 m_inverted(inverted)
Chris@189 84 {
Chris@356 85 convertMinMax(minpos, maxpos, minval, maxval, m_minlog, m_ratio);
Chris@356 86
Chris@879 87 // cerr << "LogRangeMapper: minpos " << minpos << ", maxpos "
Chris@879 88 // << maxpos << ", minval " << minval << ", maxval "
Chris@879 89 // << maxval << ", minlog " << m_minlog << ", ratio " << m_ratio
Chris@879 90 // << ", unit " << unit << endl;
Chris@356 91
Chris@189 92 assert(m_maxpos != m_minpos);
Chris@189 93
Chris@189 94 m_maxlog = (m_maxpos - m_minpos) / m_ratio + m_minlog;
Chris@879 95
Chris@879 96 // cerr << "LogRangeMapper: maxlog = " << m_maxlog << endl;
Chris@189 97 }
Chris@189 98
Chris@356 99 void
Chris@356 100 LogRangeMapper::convertMinMax(int minpos, int maxpos,
Chris@1038 101 double minval, double maxval,
Chris@1038 102 double &minlog, double &ratio)
Chris@356 103 {
Chris@1038 104 static double thresh = powf(10, -10);
Chris@356 105 if (minval < thresh) minval = thresh;
Chris@1038 106 minlog = log10(minval);
Chris@1038 107 ratio = (maxpos - minpos) / (log10(maxval) - minlog);
Chris@356 108 }
Chris@356 109
Chris@356 110 void
Chris@1038 111 LogRangeMapper::convertRatioMinLog(double ratio, double minlog,
Chris@356 112 int minpos, int maxpos,
Chris@1038 113 double &minval, double &maxval)
Chris@356 114 {
Chris@1038 115 minval = pow(10, minlog);
Chris@1038 116 maxval = pow(10, (maxpos - minpos) / ratio + minlog);
Chris@356 117 }
Chris@356 118
Chris@189 119 int
Chris@1038 120 LogRangeMapper::getPositionForValue(double value) const
Chris@189 121 {
Chris@885 122 int position = getPositionForValueUnclamped(value);
Chris@189 123 if (position < m_minpos) position = m_minpos;
Chris@189 124 if (position > m_maxpos) position = m_maxpos;
Chris@885 125 return position;
Chris@885 126 }
Chris@885 127
Chris@885 128 int
Chris@1038 129 LogRangeMapper::getPositionForValueUnclamped(double value) const
Chris@885 130 {
Chris@1038 131 static double thresh = pow(10, -10);
Chris@885 132 if (value < thresh) value = thresh;
Chris@1038 133 int position = int(lrint((log10(value) - m_minlog) * m_ratio)) + m_minpos;
Chris@879 134 if (m_inverted) return m_maxpos - (position - m_minpos);
Chris@464 135 else return position;
Chris@189 136 }
Chris@189 137
Chris@1038 138 double
Chris@189 139 LogRangeMapper::getValueForPosition(int position) const
Chris@189 140 {
Chris@879 141 if (position < m_minpos) position = m_minpos;
Chris@879 142 if (position > m_maxpos) position = m_maxpos;
Chris@1038 143 double value = getValueForPositionUnclamped(position);
Chris@885 144 return value;
Chris@885 145 }
Chris@885 146
Chris@1038 147 double
Chris@885 148 LogRangeMapper::getValueForPositionUnclamped(int position) const
Chris@885 149 {
Chris@885 150 if (m_inverted) position = m_maxpos - (position - m_minpos);
Chris@1038 151 double value = pow(10, (position - m_minpos) / m_ratio + m_minlog);
Chris@189 152 return value;
Chris@189 153 }
Chris@189 154
Chris@880 155 InterpolatingRangeMapper::InterpolatingRangeMapper(CoordMap pointMappings,
Chris@880 156 QString unit) :
Chris@880 157 m_mappings(pointMappings),
Chris@880 158 m_unit(unit)
Chris@880 159 {
Chris@880 160 for (CoordMap::const_iterator i = m_mappings.begin();
Chris@880 161 i != m_mappings.end(); ++i) {
Chris@880 162 m_reverse[i->second] = i->first;
Chris@880 163 }
Chris@880 164 }
Chris@880 165
Chris@880 166 int
Chris@1038 167 InterpolatingRangeMapper::getPositionForValue(double value) const
Chris@880 168 {
Chris@885 169 int pos = getPositionForValueUnclamped(value);
Chris@885 170 CoordMap::const_iterator i = m_mappings.begin();
Chris@885 171 if (pos < i->second) pos = i->second;
Chris@885 172 i = m_mappings.end(); --i;
Chris@885 173 if (pos > i->second) pos = i->second;
Chris@885 174 return pos;
Chris@885 175 }
Chris@880 176
Chris@885 177 int
Chris@1038 178 InterpolatingRangeMapper::getPositionForValueUnclamped(double value) const
Chris@885 179 {
Chris@1038 180 double p = interpolate(&m_mappings, value);
Chris@1038 181 return int(lrint(p));
Chris@880 182 }
Chris@880 183
Chris@1038 184 double
Chris@880 185 InterpolatingRangeMapper::getValueForPosition(int position) const
Chris@880 186 {
Chris@1038 187 double val = getValueForPositionUnclamped(position);
Chris@885 188 CoordMap::const_iterator i = m_mappings.begin();
Chris@885 189 if (val < i->first) val = i->first;
Chris@885 190 i = m_mappings.end(); --i;
Chris@885 191 if (val > i->first) val = i->first;
Chris@885 192 return val;
Chris@885 193 }
Chris@885 194
Chris@1038 195 double
Chris@885 196 InterpolatingRangeMapper::getValueForPositionUnclamped(int position) const
Chris@885 197 {
Chris@885 198 return interpolate(&m_reverse, position);
Chris@885 199 }
Chris@885 200
Chris@885 201 template <typename T>
Chris@1038 202 double
Chris@1038 203 InterpolatingRangeMapper::interpolate(T *mapping, double value) const
Chris@885 204 {
Chris@885 205 // lower_bound: first element which does not compare less than value
Chris@1038 206 typename T::const_iterator i =
Chris@1038 207 mapping->lower_bound(typename T::key_type(value));
Chris@885 208
Chris@885 209 if (i == mapping->begin()) {
Chris@885 210 // value is less than or equal to first element, so use the
Chris@885 211 // gradient from first to second and extend it
Chris@885 212 ++i;
Chris@885 213 }
Chris@885 214
Chris@885 215 if (i == mapping->end()) {
Chris@885 216 // value is off the end, so use the gradient from penultimate
Chris@885 217 // to ultimate and extend it
Chris@885 218 --i;
Chris@885 219 }
Chris@885 220
Chris@885 221 typename T::const_iterator j = i;
Chris@880 222 --j;
Chris@880 223
Chris@1038 224 double gradient = double(i->second - j->second) / double(i->first - j->first);
Chris@880 225
Chris@885 226 return j->second + (value - j->first) * gradient;
Chris@880 227 }
Chris@880 228
Chris@880 229 AutoRangeMapper::AutoRangeMapper(CoordMap pointMappings,
Chris@880 230 QString unit) :
Chris@880 231 m_mappings(pointMappings),
Chris@880 232 m_unit(unit)
Chris@880 233 {
Chris@880 234 m_type = chooseMappingTypeFor(m_mappings);
Chris@880 235
Chris@880 236 CoordMap::const_iterator first = m_mappings.begin();
Chris@880 237 CoordMap::const_iterator last = m_mappings.end();
Chris@880 238 --last;
Chris@880 239
Chris@880 240 switch (m_type) {
Chris@880 241 case StraightLine:
Chris@880 242 m_mapper = new LinearRangeMapper(first->second, last->second,
Chris@880 243 first->first, last->first,
Chris@880 244 unit, false);
Chris@880 245 break;
Chris@880 246 case Logarithmic:
Chris@880 247 m_mapper = new LogRangeMapper(first->second, last->second,
Chris@880 248 first->first, last->first,
Chris@880 249 unit, false);
Chris@880 250 break;
Chris@880 251 case Interpolating:
Chris@880 252 m_mapper = new InterpolatingRangeMapper(m_mappings, unit);
Chris@880 253 break;
Chris@880 254 }
Chris@880 255 }
Chris@880 256
Chris@880 257 AutoRangeMapper::~AutoRangeMapper()
Chris@880 258 {
Chris@880 259 delete m_mapper;
Chris@880 260 }
Chris@880 261
Chris@880 262 AutoRangeMapper::MappingType
Chris@880 263 AutoRangeMapper::chooseMappingTypeFor(const CoordMap &mappings)
Chris@880 264 {
Chris@880 265 // how do we work out whether a linear/log mapping is "close enough"?
Chris@880 266
Chris@880 267 CoordMap::const_iterator first = mappings.begin();
Chris@880 268 CoordMap::const_iterator last = mappings.end();
Chris@880 269 --last;
Chris@880 270
Chris@880 271 LinearRangeMapper linm(first->second, last->second,
Chris@880 272 first->first, last->first,
Chris@880 273 "", false);
Chris@880 274
Chris@880 275 bool inadequate = false;
Chris@880 276
Chris@880 277 for (CoordMap::const_iterator i = mappings.begin();
Chris@880 278 i != mappings.end(); ++i) {
Chris@880 279 int candidate = linm.getPositionForValue(i->first);
Chris@880 280 int diff = candidate - i->second;
Chris@880 281 if (diff < 0) diff = -diff;
Chris@880 282 if (diff > 1) {
Chris@885 283 // cerr << "AutoRangeMapper::chooseMappingTypeFor: diff = " << diff
Chris@885 284 // << ", straight-line mapping inadequate" << endl;
Chris@880 285 inadequate = true;
Chris@880 286 break;
Chris@880 287 }
Chris@880 288 }
Chris@880 289
Chris@880 290 if (!inadequate) {
Chris@880 291 return StraightLine;
Chris@880 292 }
Chris@880 293
Chris@880 294 LogRangeMapper logm(first->second, last->second,
Chris@880 295 first->first, last->first,
Chris@880 296 "", false);
Chris@880 297
Chris@880 298 inadequate = false;
Chris@880 299
Chris@880 300 for (CoordMap::const_iterator i = mappings.begin();
Chris@880 301 i != mappings.end(); ++i) {
Chris@880 302 int candidate = logm.getPositionForValue(i->first);
Chris@880 303 int diff = candidate - i->second;
Chris@880 304 if (diff < 0) diff = -diff;
Chris@880 305 if (diff > 1) {
Chris@885 306 // cerr << "AutoRangeMapper::chooseMappingTypeFor: diff = " << diff
Chris@885 307 // << ", log mapping inadequate" << endl;
Chris@880 308 inadequate = true;
Chris@880 309 break;
Chris@880 310 }
Chris@880 311 }
Chris@880 312
Chris@880 313 if (!inadequate) {
Chris@880 314 return Logarithmic;
Chris@880 315 }
Chris@880 316
Chris@880 317 return Interpolating;
Chris@880 318 }
Chris@880 319
Chris@880 320 int
Chris@1038 321 AutoRangeMapper::getPositionForValue(double value) const
Chris@880 322 {
Chris@880 323 return m_mapper->getPositionForValue(value);
Chris@880 324 }
Chris@880 325
Chris@1038 326 double
Chris@880 327 AutoRangeMapper::getValueForPosition(int position) const
Chris@880 328 {
Chris@880 329 return m_mapper->getValueForPosition(position);
Chris@880 330 }
Chris@885 331
Chris@885 332 int
Chris@1038 333 AutoRangeMapper::getPositionForValueUnclamped(double value) const
Chris@885 334 {
Chris@885 335 return m_mapper->getPositionForValueUnclamped(value);
Chris@885 336 }
Chris@885 337
Chris@1038 338 double
Chris@885 339 AutoRangeMapper::getValueForPositionUnclamped(int position) const
Chris@885 340 {
Chris@885 341 return m_mapper->getValueForPositionUnclamped(position);
Chris@885 342 }