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@190
|
41 int position = m_minpos +
|
Chris@190
|
42 lrintf(((value - m_minval) / (m_maxval - m_minval))
|
Chris@190
|
43 * (m_maxpos - m_minpos));
|
Chris@189
|
44 if (position < m_minpos) position = m_minpos;
|
Chris@189
|
45 if (position > m_maxpos) position = m_maxpos;
|
Chris@879
|
46 if (m_inverted) return m_maxpos - (position - m_minpos);
|
Chris@464
|
47 else return position;
|
Chris@189
|
48 }
|
Chris@189
|
49
|
Chris@189
|
50 float
|
Chris@189
|
51 LinearRangeMapper::getValueForPosition(int position) const
|
Chris@189
|
52 {
|
Chris@879
|
53 if (m_inverted) position = m_maxpos - (position - m_minpos);
|
Chris@879
|
54 if (position < m_minpos) position = m_minpos;
|
Chris@879
|
55 if (position > m_maxpos) position = m_maxpos;
|
Chris@190
|
56 float value = m_minval +
|
Chris@190
|
57 ((float(position - m_minpos) / float(m_maxpos - m_minpos))
|
Chris@190
|
58 * (m_maxval - m_minval));
|
Chris@189
|
59 return value;
|
Chris@189
|
60 }
|
Chris@189
|
61
|
Chris@189
|
62 LogRangeMapper::LogRangeMapper(int minpos, int maxpos,
|
Chris@356
|
63 float minval, float maxval,
|
Chris@464
|
64 QString unit, bool inverted) :
|
Chris@189
|
65 m_minpos(minpos),
|
Chris@189
|
66 m_maxpos(maxpos),
|
Chris@464
|
67 m_unit(unit),
|
Chris@464
|
68 m_inverted(inverted)
|
Chris@189
|
69 {
|
Chris@356
|
70 convertMinMax(minpos, maxpos, minval, maxval, m_minlog, m_ratio);
|
Chris@356
|
71
|
Chris@879
|
72 // cerr << "LogRangeMapper: minpos " << minpos << ", maxpos "
|
Chris@879
|
73 // << maxpos << ", minval " << minval << ", maxval "
|
Chris@879
|
74 // << maxval << ", minlog " << m_minlog << ", ratio " << m_ratio
|
Chris@879
|
75 // << ", unit " << unit << endl;
|
Chris@356
|
76
|
Chris@189
|
77 assert(m_maxpos != m_minpos);
|
Chris@189
|
78
|
Chris@189
|
79 m_maxlog = (m_maxpos - m_minpos) / m_ratio + m_minlog;
|
Chris@879
|
80
|
Chris@879
|
81 // cerr << "LogRangeMapper: maxlog = " << m_maxlog << endl;
|
Chris@189
|
82 }
|
Chris@189
|
83
|
Chris@356
|
84 void
|
Chris@356
|
85 LogRangeMapper::convertMinMax(int minpos, int maxpos,
|
Chris@356
|
86 float minval, float maxval,
|
Chris@356
|
87 float &minlog, float &ratio)
|
Chris@356
|
88 {
|
Chris@356
|
89 static float thresh = powf(10, -10);
|
Chris@356
|
90 if (minval < thresh) minval = thresh;
|
Chris@356
|
91 minlog = log10f(minval);
|
Chris@356
|
92 ratio = (maxpos - minpos) / (log10f(maxval) - minlog);
|
Chris@356
|
93 }
|
Chris@356
|
94
|
Chris@356
|
95 void
|
Chris@356
|
96 LogRangeMapper::convertRatioMinLog(float ratio, float minlog,
|
Chris@356
|
97 int minpos, int maxpos,
|
Chris@356
|
98 float &minval, float &maxval)
|
Chris@356
|
99 {
|
Chris@356
|
100 minval = powf(10, minlog);
|
Chris@356
|
101 maxval = powf(10, (maxpos - minpos) / ratio + minlog);
|
Chris@356
|
102 }
|
Chris@356
|
103
|
Chris@189
|
104 int
|
Chris@189
|
105 LogRangeMapper::getPositionForValue(float value) const
|
Chris@189
|
106 {
|
Chris@879
|
107 int position = lrintf((log10(value) - m_minlog) * m_ratio) + m_minpos;
|
Chris@189
|
108 if (position < m_minpos) position = m_minpos;
|
Chris@189
|
109 if (position > m_maxpos) position = m_maxpos;
|
Chris@879
|
110 // cerr << "LogRangeMapper::getPositionForValue: " << value << " -> "
|
Chris@687
|
111 // << position << " (minpos " << m_minpos << ", maxpos " << m_maxpos << ", ratio " << m_ratio << ", minlog " << m_minlog << ")" << endl;
|
Chris@879
|
112 if (m_inverted) return m_maxpos - (position - m_minpos);
|
Chris@464
|
113 else return position;
|
Chris@189
|
114 }
|
Chris@189
|
115
|
Chris@189
|
116 float
|
Chris@189
|
117 LogRangeMapper::getValueForPosition(int position) const
|
Chris@189
|
118 {
|
Chris@879
|
119 if (m_inverted) position = m_maxpos - (position - m_minpos);
|
Chris@879
|
120 if (position < m_minpos) position = m_minpos;
|
Chris@879
|
121 if (position > m_maxpos) position = m_maxpos;
|
Chris@189
|
122 float value = powf(10, (position - m_minpos) / m_ratio + m_minlog);
|
Chris@879
|
123 // cerr << "LogRangeMapper::getValueForPosition: " << position << " -> "
|
Chris@687
|
124 // << value << " (minpos " << m_minpos << ", maxpos " << m_maxpos << ", ratio " << m_ratio << ", minlog " << m_minlog << ")" << endl;
|
Chris@189
|
125 return value;
|
Chris@189
|
126 }
|
Chris@189
|
127
|
Chris@880
|
128 InterpolatingRangeMapper::InterpolatingRangeMapper(CoordMap pointMappings,
|
Chris@880
|
129 QString unit) :
|
Chris@880
|
130 m_mappings(pointMappings),
|
Chris@880
|
131 m_unit(unit)
|
Chris@880
|
132 {
|
Chris@880
|
133 for (CoordMap::const_iterator i = m_mappings.begin();
|
Chris@880
|
134 i != m_mappings.end(); ++i) {
|
Chris@880
|
135 m_reverse[i->second] = i->first;
|
Chris@880
|
136 }
|
Chris@880
|
137 }
|
Chris@880
|
138
|
Chris@880
|
139 int
|
Chris@880
|
140 InterpolatingRangeMapper::getPositionForValue(float value) const
|
Chris@880
|
141 {
|
Chris@880
|
142 CoordMap::const_iterator i = m_mappings.lower_bound(value);
|
Chris@880
|
143 CoordMap::const_iterator j = i;
|
Chris@880
|
144 --j;
|
Chris@880
|
145
|
Chris@880
|
146 if (i == m_mappings.end()) return j->second;
|
Chris@880
|
147 if (i == m_mappings.begin()) return i->second;
|
Chris@880
|
148 if (i->first == value) return i->second;
|
Chris@880
|
149
|
Chris@880
|
150 return lrintf(((value - j->first) / (i->first - j->first)) *
|
Chris@880
|
151 float(i->second - j->second) + j->second);
|
Chris@880
|
152 }
|
Chris@880
|
153
|
Chris@880
|
154 float
|
Chris@880
|
155 InterpolatingRangeMapper::getValueForPosition(int position) const
|
Chris@880
|
156 {
|
Chris@880
|
157 std::map<int, float>::const_iterator i = m_reverse.lower_bound(position);
|
Chris@880
|
158 std::map<int, float>::const_iterator j = i;
|
Chris@880
|
159 --j;
|
Chris@880
|
160
|
Chris@880
|
161 if (i == m_reverse.end()) return j->second;
|
Chris@880
|
162 if (i == m_reverse.begin()) return i->second;
|
Chris@880
|
163 if (i->first == position) return i->second;
|
Chris@880
|
164
|
Chris@880
|
165 return ((float(position) - j->first) / (i->first - j->first)) *
|
Chris@880
|
166 (i->second - j->second) + j->second;
|
Chris@880
|
167 }
|
Chris@880
|
168
|
Chris@880
|
169 AutoRangeMapper::AutoRangeMapper(CoordMap pointMappings,
|
Chris@880
|
170 QString unit) :
|
Chris@880
|
171 m_mappings(pointMappings),
|
Chris@880
|
172 m_unit(unit)
|
Chris@880
|
173 {
|
Chris@880
|
174 m_type = chooseMappingTypeFor(m_mappings);
|
Chris@880
|
175
|
Chris@880
|
176 CoordMap::const_iterator first = m_mappings.begin();
|
Chris@880
|
177 CoordMap::const_iterator last = m_mappings.end();
|
Chris@880
|
178 --last;
|
Chris@880
|
179
|
Chris@880
|
180 switch (m_type) {
|
Chris@880
|
181 case StraightLine:
|
Chris@880
|
182 m_mapper = new LinearRangeMapper(first->second, last->second,
|
Chris@880
|
183 first->first, last->first,
|
Chris@880
|
184 unit, false);
|
Chris@880
|
185 break;
|
Chris@880
|
186 case Logarithmic:
|
Chris@880
|
187 m_mapper = new LogRangeMapper(first->second, last->second,
|
Chris@880
|
188 first->first, last->first,
|
Chris@880
|
189 unit, false);
|
Chris@880
|
190 break;
|
Chris@880
|
191 case Interpolating:
|
Chris@880
|
192 m_mapper = new InterpolatingRangeMapper(m_mappings, unit);
|
Chris@880
|
193 break;
|
Chris@880
|
194 }
|
Chris@880
|
195 }
|
Chris@880
|
196
|
Chris@880
|
197 AutoRangeMapper::~AutoRangeMapper()
|
Chris@880
|
198 {
|
Chris@880
|
199 delete m_mapper;
|
Chris@880
|
200 }
|
Chris@880
|
201
|
Chris@880
|
202 AutoRangeMapper::MappingType
|
Chris@880
|
203 AutoRangeMapper::chooseMappingTypeFor(const CoordMap &mappings)
|
Chris@880
|
204 {
|
Chris@880
|
205 // how do we work out whether a linear/log mapping is "close enough"?
|
Chris@880
|
206
|
Chris@880
|
207 CoordMap::const_iterator first = mappings.begin();
|
Chris@880
|
208 CoordMap::const_iterator last = mappings.end();
|
Chris@880
|
209 --last;
|
Chris@880
|
210
|
Chris@880
|
211 LinearRangeMapper linm(first->second, last->second,
|
Chris@880
|
212 first->first, last->first,
|
Chris@880
|
213 "", false);
|
Chris@880
|
214
|
Chris@880
|
215 bool inadequate = false;
|
Chris@880
|
216
|
Chris@880
|
217 for (CoordMap::const_iterator i = mappings.begin();
|
Chris@880
|
218 i != mappings.end(); ++i) {
|
Chris@880
|
219 int candidate = linm.getPositionForValue(i->first);
|
Chris@880
|
220 int diff = candidate - i->second;
|
Chris@880
|
221 if (diff < 0) diff = -diff;
|
Chris@880
|
222 if (diff > 1) {
|
Chris@880
|
223 cerr << "AutoRangeMapper::chooseMappingTypeFor: diff = " << diff
|
Chris@880
|
224 << ", straight-line mapping inadequate" << endl;
|
Chris@880
|
225 inadequate = true;
|
Chris@880
|
226 break;
|
Chris@880
|
227 }
|
Chris@880
|
228 }
|
Chris@880
|
229
|
Chris@880
|
230 if (!inadequate) {
|
Chris@880
|
231 return StraightLine;
|
Chris@880
|
232 }
|
Chris@880
|
233
|
Chris@880
|
234 LogRangeMapper logm(first->second, last->second,
|
Chris@880
|
235 first->first, last->first,
|
Chris@880
|
236 "", false);
|
Chris@880
|
237
|
Chris@880
|
238 inadequate = false;
|
Chris@880
|
239
|
Chris@880
|
240 for (CoordMap::const_iterator i = mappings.begin();
|
Chris@880
|
241 i != mappings.end(); ++i) {
|
Chris@880
|
242 int candidate = logm.getPositionForValue(i->first);
|
Chris@880
|
243 int diff = candidate - i->second;
|
Chris@880
|
244 if (diff < 0) diff = -diff;
|
Chris@880
|
245 if (diff > 1) {
|
Chris@880
|
246 cerr << "AutoRangeMapper::chooseMappingTypeFor: diff = " << diff
|
Chris@880
|
247 << ", log mapping inadequate" << endl;
|
Chris@880
|
248 inadequate = true;
|
Chris@880
|
249 break;
|
Chris@880
|
250 }
|
Chris@880
|
251 }
|
Chris@880
|
252
|
Chris@880
|
253 if (!inadequate) {
|
Chris@880
|
254 return Logarithmic;
|
Chris@880
|
255 }
|
Chris@880
|
256
|
Chris@880
|
257 return Interpolating;
|
Chris@880
|
258 }
|
Chris@880
|
259
|
Chris@880
|
260 int
|
Chris@880
|
261 AutoRangeMapper::getPositionForValue(float value) const
|
Chris@880
|
262 {
|
Chris@880
|
263 return m_mapper->getPositionForValue(value);
|
Chris@880
|
264 }
|
Chris@880
|
265
|
Chris@880
|
266 float
|
Chris@880
|
267 AutoRangeMapper::getValueForPosition(int position) const
|
Chris@880
|
268 {
|
Chris@880
|
269 return m_mapper->getValueForPosition(position);
|
Chris@880
|
270 }
|