annotate base/RangeMapper.cpp @ 1196:c7b9c902642f 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 cc27f35aa75c
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 }