annotate base/RangeMapper.cpp @ 892:451f7f3ab6e7

Make octave numbering configurable, and change default to C4 = middle C
author Chris Cannam
date Thu, 27 Mar 2014 13:32:56 +0000
parents 12a6140b3ae0
children cc27f35aa75c
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@189 25 float minval, float 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@189 39 LinearRangeMapper::getPositionForValue(float 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@885 48 LinearRangeMapper::getPositionForValueUnclamped(float value) const
Chris@885 49 {
Chris@190 50 int position = m_minpos +
Chris@190 51 lrintf(((value - m_minval) / (m_maxval - m_minval))
Chris@190 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@189 57 float
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@885 62 float value = getValueForPositionUnclamped(position);
Chris@885 63 return value;
Chris@885 64 }
Chris@885 65
Chris@885 66 float
Chris@885 67 LinearRangeMapper::getValueForPositionUnclamped(int position) const
Chris@885 68 {
Chris@885 69 if (m_inverted) position = m_maxpos - (position - m_minpos);
Chris@190 70 float value = m_minval +
Chris@190 71 ((float(position - m_minpos) / float(m_maxpos - m_minpos))
Chris@190 72 * (m_maxval - m_minval));
Chris@189 73 return value;
Chris@189 74 }
Chris@189 75
Chris@189 76 LogRangeMapper::LogRangeMapper(int minpos, int maxpos,
Chris@356 77 float minval, float maxval,
Chris@464 78 QString unit, bool inverted) :
Chris@189 79 m_minpos(minpos),
Chris@189 80 m_maxpos(maxpos),
Chris@464 81 m_unit(unit),
Chris@464 82 m_inverted(inverted)
Chris@189 83 {
Chris@356 84 convertMinMax(minpos, maxpos, minval, maxval, m_minlog, m_ratio);
Chris@356 85
Chris@879 86 // cerr << "LogRangeMapper: minpos " << minpos << ", maxpos "
Chris@879 87 // << maxpos << ", minval " << minval << ", maxval "
Chris@879 88 // << maxval << ", minlog " << m_minlog << ", ratio " << m_ratio
Chris@879 89 // << ", unit " << unit << endl;
Chris@356 90
Chris@189 91 assert(m_maxpos != m_minpos);
Chris@189 92
Chris@189 93 m_maxlog = (m_maxpos - m_minpos) / m_ratio + m_minlog;
Chris@879 94
Chris@879 95 // cerr << "LogRangeMapper: maxlog = " << m_maxlog << endl;
Chris@189 96 }
Chris@189 97
Chris@356 98 void
Chris@356 99 LogRangeMapper::convertMinMax(int minpos, int maxpos,
Chris@356 100 float minval, float maxval,
Chris@356 101 float &minlog, float &ratio)
Chris@356 102 {
Chris@356 103 static float thresh = powf(10, -10);
Chris@356 104 if (minval < thresh) minval = thresh;
Chris@356 105 minlog = log10f(minval);
Chris@356 106 ratio = (maxpos - minpos) / (log10f(maxval) - minlog);
Chris@356 107 }
Chris@356 108
Chris@356 109 void
Chris@356 110 LogRangeMapper::convertRatioMinLog(float ratio, float minlog,
Chris@356 111 int minpos, int maxpos,
Chris@356 112 float &minval, float &maxval)
Chris@356 113 {
Chris@356 114 minval = powf(10, minlog);
Chris@356 115 maxval = powf(10, (maxpos - minpos) / ratio + minlog);
Chris@356 116 }
Chris@356 117
Chris@189 118 int
Chris@189 119 LogRangeMapper::getPositionForValue(float value) const
Chris@189 120 {
Chris@885 121 int position = getPositionForValueUnclamped(value);
Chris@189 122 if (position < m_minpos) position = m_minpos;
Chris@189 123 if (position > m_maxpos) position = m_maxpos;
Chris@885 124 return position;
Chris@885 125 }
Chris@885 126
Chris@885 127 int
Chris@885 128 LogRangeMapper::getPositionForValueUnclamped(float value) const
Chris@885 129 {
Chris@885 130 static float thresh = powf(10, -10);
Chris@885 131 if (value < thresh) value = thresh;
Chris@885 132 int position = lrintf((log10(value) - m_minlog) * m_ratio) + m_minpos;
Chris@879 133 if (m_inverted) return m_maxpos - (position - m_minpos);
Chris@464 134 else return position;
Chris@189 135 }
Chris@189 136
Chris@189 137 float
Chris@189 138 LogRangeMapper::getValueForPosition(int position) const
Chris@189 139 {
Chris@879 140 if (position < m_minpos) position = m_minpos;
Chris@879 141 if (position > m_maxpos) position = m_maxpos;
Chris@885 142 float value = getValueForPositionUnclamped(position);
Chris@885 143 return value;
Chris@885 144 }
Chris@885 145
Chris@885 146 float
Chris@885 147 LogRangeMapper::getValueForPositionUnclamped(int position) const
Chris@885 148 {
Chris@885 149 if (m_inverted) position = m_maxpos - (position - m_minpos);
Chris@189 150 float value = powf(10, (position - m_minpos) / m_ratio + m_minlog);
Chris@189 151 return value;
Chris@189 152 }
Chris@189 153
Chris@880 154 InterpolatingRangeMapper::InterpolatingRangeMapper(CoordMap pointMappings,
Chris@880 155 QString unit) :
Chris@880 156 m_mappings(pointMappings),
Chris@880 157 m_unit(unit)
Chris@880 158 {
Chris@880 159 for (CoordMap::const_iterator i = m_mappings.begin();
Chris@880 160 i != m_mappings.end(); ++i) {
Chris@880 161 m_reverse[i->second] = i->first;
Chris@880 162 }
Chris@880 163 }
Chris@880 164
Chris@880 165 int
Chris@880 166 InterpolatingRangeMapper::getPositionForValue(float value) const
Chris@880 167 {
Chris@885 168 int pos = getPositionForValueUnclamped(value);
Chris@885 169 CoordMap::const_iterator i = m_mappings.begin();
Chris@885 170 if (pos < i->second) pos = i->second;
Chris@885 171 i = m_mappings.end(); --i;
Chris@885 172 if (pos > i->second) pos = i->second;
Chris@885 173 return pos;
Chris@885 174 }
Chris@880 175
Chris@885 176 int
Chris@885 177 InterpolatingRangeMapper::getPositionForValueUnclamped(float value) const
Chris@885 178 {
Chris@885 179 float p = interpolate(&m_mappings, value);
Chris@885 180 return lrintf(p);
Chris@880 181 }
Chris@880 182
Chris@880 183 float
Chris@880 184 InterpolatingRangeMapper::getValueForPosition(int position) const
Chris@880 185 {
Chris@885 186 float val = getValueForPositionUnclamped(position);
Chris@885 187 CoordMap::const_iterator i = m_mappings.begin();
Chris@885 188 if (val < i->first) val = i->first;
Chris@885 189 i = m_mappings.end(); --i;
Chris@885 190 if (val > i->first) val = i->first;
Chris@885 191 return val;
Chris@885 192 }
Chris@885 193
Chris@885 194 float
Chris@885 195 InterpolatingRangeMapper::getValueForPositionUnclamped(int position) const
Chris@885 196 {
Chris@885 197 return interpolate(&m_reverse, position);
Chris@885 198 }
Chris@885 199
Chris@885 200 template <typename T>
Chris@885 201 float
Chris@885 202 InterpolatingRangeMapper::interpolate(T *mapping, float value) const
Chris@885 203 {
Chris@885 204 // lower_bound: first element which does not compare less than value
Chris@885 205 typename T::const_iterator i = mapping->lower_bound(value);
Chris@885 206
Chris@885 207 if (i == mapping->begin()) {
Chris@885 208 // value is less than or equal to first element, so use the
Chris@885 209 // gradient from first to second and extend it
Chris@885 210 ++i;
Chris@885 211 }
Chris@885 212
Chris@885 213 if (i == mapping->end()) {
Chris@885 214 // value is off the end, so use the gradient from penultimate
Chris@885 215 // to ultimate and extend it
Chris@885 216 --i;
Chris@885 217 }
Chris@885 218
Chris@885 219 typename T::const_iterator j = i;
Chris@880 220 --j;
Chris@880 221
Chris@885 222 float gradient = float(i->second - j->second) / float(i->first - j->first);
Chris@880 223
Chris@885 224 return j->second + (value - j->first) * gradient;
Chris@880 225 }
Chris@880 226
Chris@880 227 AutoRangeMapper::AutoRangeMapper(CoordMap pointMappings,
Chris@880 228 QString unit) :
Chris@880 229 m_mappings(pointMappings),
Chris@880 230 m_unit(unit)
Chris@880 231 {
Chris@880 232 m_type = chooseMappingTypeFor(m_mappings);
Chris@880 233
Chris@880 234 CoordMap::const_iterator first = m_mappings.begin();
Chris@880 235 CoordMap::const_iterator last = m_mappings.end();
Chris@880 236 --last;
Chris@880 237
Chris@880 238 switch (m_type) {
Chris@880 239 case StraightLine:
Chris@880 240 m_mapper = new LinearRangeMapper(first->second, last->second,
Chris@880 241 first->first, last->first,
Chris@880 242 unit, false);
Chris@880 243 break;
Chris@880 244 case Logarithmic:
Chris@880 245 m_mapper = new LogRangeMapper(first->second, last->second,
Chris@880 246 first->first, last->first,
Chris@880 247 unit, false);
Chris@880 248 break;
Chris@880 249 case Interpolating:
Chris@880 250 m_mapper = new InterpolatingRangeMapper(m_mappings, unit);
Chris@880 251 break;
Chris@880 252 }
Chris@880 253 }
Chris@880 254
Chris@880 255 AutoRangeMapper::~AutoRangeMapper()
Chris@880 256 {
Chris@880 257 delete m_mapper;
Chris@880 258 }
Chris@880 259
Chris@880 260 AutoRangeMapper::MappingType
Chris@880 261 AutoRangeMapper::chooseMappingTypeFor(const CoordMap &mappings)
Chris@880 262 {
Chris@880 263 // how do we work out whether a linear/log mapping is "close enough"?
Chris@880 264
Chris@880 265 CoordMap::const_iterator first = mappings.begin();
Chris@880 266 CoordMap::const_iterator last = mappings.end();
Chris@880 267 --last;
Chris@880 268
Chris@880 269 LinearRangeMapper linm(first->second, last->second,
Chris@880 270 first->first, last->first,
Chris@880 271 "", false);
Chris@880 272
Chris@880 273 bool inadequate = false;
Chris@880 274
Chris@880 275 for (CoordMap::const_iterator i = mappings.begin();
Chris@880 276 i != mappings.end(); ++i) {
Chris@880 277 int candidate = linm.getPositionForValue(i->first);
Chris@880 278 int diff = candidate - i->second;
Chris@880 279 if (diff < 0) diff = -diff;
Chris@880 280 if (diff > 1) {
Chris@885 281 // cerr << "AutoRangeMapper::chooseMappingTypeFor: diff = " << diff
Chris@885 282 // << ", straight-line mapping inadequate" << endl;
Chris@880 283 inadequate = true;
Chris@880 284 break;
Chris@880 285 }
Chris@880 286 }
Chris@880 287
Chris@880 288 if (!inadequate) {
Chris@880 289 return StraightLine;
Chris@880 290 }
Chris@880 291
Chris@880 292 LogRangeMapper logm(first->second, last->second,
Chris@880 293 first->first, last->first,
Chris@880 294 "", false);
Chris@880 295
Chris@880 296 inadequate = false;
Chris@880 297
Chris@880 298 for (CoordMap::const_iterator i = mappings.begin();
Chris@880 299 i != mappings.end(); ++i) {
Chris@880 300 int candidate = logm.getPositionForValue(i->first);
Chris@880 301 int diff = candidate - i->second;
Chris@880 302 if (diff < 0) diff = -diff;
Chris@880 303 if (diff > 1) {
Chris@885 304 // cerr << "AutoRangeMapper::chooseMappingTypeFor: diff = " << diff
Chris@885 305 // << ", log mapping inadequate" << endl;
Chris@880 306 inadequate = true;
Chris@880 307 break;
Chris@880 308 }
Chris@880 309 }
Chris@880 310
Chris@880 311 if (!inadequate) {
Chris@880 312 return Logarithmic;
Chris@880 313 }
Chris@880 314
Chris@880 315 return Interpolating;
Chris@880 316 }
Chris@880 317
Chris@880 318 int
Chris@880 319 AutoRangeMapper::getPositionForValue(float value) const
Chris@880 320 {
Chris@880 321 return m_mapper->getPositionForValue(value);
Chris@880 322 }
Chris@880 323
Chris@880 324 float
Chris@880 325 AutoRangeMapper::getValueForPosition(int position) const
Chris@880 326 {
Chris@880 327 return m_mapper->getValueForPosition(position);
Chris@880 328 }
Chris@885 329
Chris@885 330 int
Chris@885 331 AutoRangeMapper::getPositionForValueUnclamped(float value) const
Chris@885 332 {
Chris@885 333 return m_mapper->getPositionForValueUnclamped(value);
Chris@885 334 }
Chris@885 335
Chris@885 336 float
Chris@885 337 AutoRangeMapper::getValueForPositionUnclamped(int position) const
Chris@885 338 {
Chris@885 339 return m_mapper->getValueForPositionUnclamped(position);
Chris@885 340 }