Chris@49
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@0
|
2
|
Chris@0
|
3 /*
|
Chris@52
|
4 Sonic Visualiser
|
Chris@52
|
5 An audio file viewer and annotation editor.
|
Chris@52
|
6 Centre for Digital Music, Queen Mary, University of London.
|
Chris@0
|
7
|
Chris@52
|
8 This program is free software; you can redistribute it and/or
|
Chris@52
|
9 modify it under the terms of the GNU General Public License as
|
Chris@52
|
10 published by the Free Software Foundation; either version 2 of the
|
Chris@52
|
11 License, or (at your option) any later version. See the file
|
Chris@52
|
12 COPYING included with this distribution for more information.
|
Chris@0
|
13 */
|
Chris@0
|
14
|
Chris@0
|
15 /*
|
Chris@0
|
16 This is a modified version of a source file from the
|
Chris@0
|
17 Rosegarden MIDI and audio sequencer and notation editor.
|
Chris@17
|
18 This file copyright 2000-2006 Chris Cannam.
|
Chris@0
|
19 */
|
Chris@0
|
20
|
Chris@0
|
21 #include "base/AudioLevel.h"
|
Chris@0
|
22 #include <cmath>
|
Chris@0
|
23 #include <iostream>
|
Chris@0
|
24 #include <map>
|
Chris@0
|
25 #include <vector>
|
Chris@0
|
26 #include <cassert>
|
Chris@0
|
27
|
Chris@0
|
28 const float AudioLevel::DB_FLOOR = -1000.0;
|
Chris@0
|
29
|
Chris@0
|
30 struct FaderDescription
|
Chris@0
|
31 {
|
Chris@0
|
32 FaderDescription(float _minDb, float _maxDb, float _zeroPoint) :
|
Chris@0
|
33 minDb(_minDb), maxDb(_maxDb), zeroPoint(_zeroPoint) { }
|
Chris@0
|
34
|
Chris@0
|
35 float minDb;
|
Chris@0
|
36 float maxDb;
|
Chris@0
|
37 float zeroPoint; // as fraction of total throw
|
Chris@0
|
38 };
|
Chris@0
|
39
|
Chris@0
|
40 static const FaderDescription faderTypes[] = {
|
Chris@0
|
41 FaderDescription(-40.0, +6.0, 0.75), // short
|
Chris@0
|
42 FaderDescription(-70.0, +10.0, 0.80), // long
|
Chris@0
|
43 FaderDescription(-70.0, 0.0, 1.00), // IEC268
|
Chris@0
|
44 FaderDescription(-70.0, +10.0, 0.80), // IEC268 long
|
Chris@0
|
45 FaderDescription(-40.0, 0.0, 1.00), // preview
|
Chris@0
|
46 };
|
Chris@0
|
47
|
Chris@0
|
48 typedef std::vector<float> LevelList;
|
Chris@0
|
49 static std::map<int, LevelList> previewLevelCache;
|
Chris@0
|
50 static const LevelList &getPreviewLevelCache(int levels);
|
Chris@0
|
51
|
Chris@0
|
52 float
|
Chris@0
|
53 AudioLevel::multiplier_to_dB(float multiplier)
|
Chris@0
|
54 {
|
Chris@0
|
55 if (multiplier == 0.0) return DB_FLOOR;
|
Chris@0
|
56 float dB = 10 * log10f(multiplier);
|
Chris@0
|
57 return dB;
|
Chris@0
|
58 }
|
Chris@0
|
59
|
Chris@0
|
60 float
|
Chris@0
|
61 AudioLevel::dB_to_multiplier(float dB)
|
Chris@0
|
62 {
|
Chris@0
|
63 if (dB == DB_FLOOR) return 0.0;
|
Chris@0
|
64 float m = powf(10.0, dB / 10.0);
|
Chris@0
|
65 return m;
|
Chris@0
|
66 }
|
Chris@0
|
67
|
Chris@0
|
68 /* IEC 60-268-18 fader levels. Thanks to Steve Harris. */
|
Chris@0
|
69
|
Chris@0
|
70 static float iec_dB_to_fader(float db)
|
Chris@0
|
71 {
|
Chris@0
|
72 float def = 0.0f; // Meter deflection %age
|
Chris@0
|
73
|
Chris@0
|
74 if (db < -70.0f) {
|
Chris@0
|
75 def = 0.0f;
|
Chris@0
|
76 } else if (db < -60.0f) {
|
Chris@0
|
77 def = (db + 70.0f) * 0.25f;
|
Chris@0
|
78 } else if (db < -50.0f) {
|
Chris@0
|
79 def = (db + 60.0f) * 0.5f + 5.0f;
|
Chris@0
|
80 } else if (db < -40.0f) {
|
Chris@0
|
81 def = (db + 50.0f) * 0.75f + 7.5f;
|
Chris@0
|
82 } else if (db < -30.0f) {
|
Chris@0
|
83 def = (db + 40.0f) * 1.5f + 15.0f;
|
Chris@0
|
84 } else if (db < -20.0f) {
|
Chris@0
|
85 def = (db + 30.0f) * 2.0f + 30.0f;
|
Chris@0
|
86 } else {
|
Chris@0
|
87 def = (db + 20.0f) * 2.5f + 50.0f;
|
Chris@0
|
88 }
|
Chris@0
|
89
|
Chris@0
|
90 return def;
|
Chris@0
|
91 }
|
Chris@0
|
92
|
Chris@0
|
93 static float iec_fader_to_dB(float def) // Meter deflection %age
|
Chris@0
|
94 {
|
Chris@0
|
95 float db = 0.0f;
|
Chris@0
|
96
|
Chris@0
|
97 if (def >= 50.0f) {
|
Chris@0
|
98 db = (def - 50.0f) / 2.5f - 20.0f;
|
Chris@0
|
99 } else if (def >= 30.0f) {
|
Chris@0
|
100 db = (def - 30.0f) / 2.0f - 30.0f;
|
Chris@0
|
101 } else if (def >= 15.0f) {
|
Chris@0
|
102 db = (def - 15.0f) / 1.5f - 40.0f;
|
Chris@0
|
103 } else if (def >= 7.5f) {
|
Chris@0
|
104 db = (def - 7.5f) / 0.75f - 50.0f;
|
Chris@0
|
105 } else if (def >= 5.0f) {
|
Chris@0
|
106 db = (def - 5.0f) / 0.5f - 60.0f;
|
Chris@0
|
107 } else {
|
Chris@0
|
108 db = (def / 0.25f) - 70.0f;
|
Chris@0
|
109 }
|
Chris@0
|
110
|
Chris@0
|
111 return db;
|
Chris@0
|
112 }
|
Chris@0
|
113
|
Chris@0
|
114 float
|
Chris@0
|
115 AudioLevel::fader_to_dB(int level, int maxLevel, FaderType type)
|
Chris@0
|
116 {
|
Chris@0
|
117 if (level == 0) return DB_FLOOR;
|
Chris@0
|
118
|
Chris@0
|
119 if (type == IEC268Meter || type == IEC268LongMeter) {
|
Chris@0
|
120
|
Chris@0
|
121 float maxPercent = iec_dB_to_fader(faderTypes[type].maxDb);
|
Chris@0
|
122 float percent = float(level) * maxPercent / float(maxLevel);
|
Chris@0
|
123 float dB = iec_fader_to_dB(percent);
|
Chris@0
|
124 return dB;
|
Chris@0
|
125
|
Chris@0
|
126 } else { // scale proportional to sqrt(fabs(dB))
|
Chris@0
|
127
|
Chris@0
|
128 int zeroLevel = int(maxLevel * faderTypes[type].zeroPoint);
|
Chris@0
|
129
|
Chris@0
|
130 if (level >= zeroLevel) {
|
Chris@0
|
131
|
Chris@0
|
132 float value = level - zeroLevel;
|
Chris@0
|
133 float scale = float(maxLevel - zeroLevel) /
|
Chris@0
|
134 sqrtf(faderTypes[type].maxDb);
|
Chris@0
|
135 value /= scale;
|
Chris@0
|
136 float dB = powf(value, 2.0);
|
Chris@0
|
137 return dB;
|
Chris@0
|
138
|
Chris@0
|
139 } else {
|
Chris@0
|
140
|
Chris@0
|
141 float value = zeroLevel - level;
|
Chris@0
|
142 float scale = zeroLevel / sqrtf(0.0 - faderTypes[type].minDb);
|
Chris@0
|
143 value /= scale;
|
Chris@0
|
144 float dB = powf(value, 2.0);
|
Chris@0
|
145 return 0.0 - dB;
|
Chris@0
|
146 }
|
Chris@0
|
147 }
|
Chris@0
|
148 }
|
Chris@0
|
149
|
Chris@0
|
150
|
Chris@0
|
151 int
|
Chris@0
|
152 AudioLevel::dB_to_fader(float dB, int maxLevel, FaderType type)
|
Chris@0
|
153 {
|
Chris@0
|
154 if (dB == DB_FLOOR) return 0;
|
Chris@0
|
155
|
Chris@0
|
156 if (type == IEC268Meter || type == IEC268LongMeter) {
|
Chris@0
|
157
|
Chris@0
|
158 // The IEC scale gives a "percentage travel" for a given dB
|
Chris@0
|
159 // level, but it reaches 100% at 0dB. So we want to treat the
|
Chris@0
|
160 // result not as a percentage, but as a scale between 0 and
|
Chris@0
|
161 // whatever the "percentage" for our (possibly >0dB) max dB is.
|
Chris@0
|
162
|
Chris@0
|
163 float maxPercent = iec_dB_to_fader(faderTypes[type].maxDb);
|
Chris@0
|
164 float percent = iec_dB_to_fader(dB);
|
Chris@0
|
165 int faderLevel = int((maxLevel * percent) / maxPercent + 0.01);
|
Chris@0
|
166
|
Chris@0
|
167 if (faderLevel < 0) faderLevel = 0;
|
Chris@0
|
168 if (faderLevel > maxLevel) faderLevel = maxLevel;
|
Chris@0
|
169 return faderLevel;
|
Chris@0
|
170
|
Chris@0
|
171 } else {
|
Chris@0
|
172
|
Chris@0
|
173 int zeroLevel = int(maxLevel * faderTypes[type].zeroPoint);
|
Chris@0
|
174
|
Chris@0
|
175 if (dB >= 0.0) {
|
Chris@0
|
176
|
Chris@0
|
177 float value = sqrtf(dB);
|
Chris@0
|
178 float scale = (maxLevel - zeroLevel) / sqrtf(faderTypes[type].maxDb);
|
Chris@0
|
179 value *= scale;
|
Chris@0
|
180 int level = int(value + 0.01) + zeroLevel;
|
Chris@0
|
181 if (level > maxLevel) level = maxLevel;
|
Chris@0
|
182 return level;
|
Chris@0
|
183
|
Chris@0
|
184 } else {
|
Chris@0
|
185
|
Chris@0
|
186 dB = 0.0 - dB;
|
Chris@0
|
187 float value = sqrtf(dB);
|
Chris@0
|
188 float scale = zeroLevel / sqrtf(0.0 - faderTypes[type].minDb);
|
Chris@0
|
189 value *= scale;
|
Chris@0
|
190 int level = zeroLevel - int(value + 0.01);
|
Chris@0
|
191 if (level < 0) level = 0;
|
Chris@0
|
192 return level;
|
Chris@0
|
193 }
|
Chris@0
|
194 }
|
Chris@0
|
195 }
|
Chris@0
|
196
|
Chris@0
|
197
|
Chris@0
|
198 float
|
Chris@0
|
199 AudioLevel::fader_to_multiplier(int level, int maxLevel, FaderType type)
|
Chris@0
|
200 {
|
Chris@0
|
201 if (level == 0) return 0.0;
|
Chris@0
|
202 return dB_to_multiplier(fader_to_dB(level, maxLevel, type));
|
Chris@0
|
203 }
|
Chris@0
|
204
|
Chris@0
|
205 int
|
Chris@0
|
206 AudioLevel::multiplier_to_fader(float multiplier, int maxLevel, FaderType type)
|
Chris@0
|
207 {
|
Chris@0
|
208 if (multiplier == 0.0) return 0;
|
Chris@0
|
209 float dB = multiplier_to_dB(multiplier);
|
Chris@0
|
210 int fader = dB_to_fader(dB, maxLevel, type);
|
Chris@0
|
211 return fader;
|
Chris@0
|
212 }
|
Chris@0
|
213
|
Chris@0
|
214
|
Chris@0
|
215 const LevelList &
|
Chris@0
|
216 getPreviewLevelCache(int levels)
|
Chris@0
|
217 {
|
Chris@0
|
218 LevelList &ll = previewLevelCache[levels];
|
Chris@0
|
219 if (ll.empty()) {
|
Chris@0
|
220 for (int i = 0; i <= levels; ++i) {
|
Chris@0
|
221 float m = AudioLevel::fader_to_multiplier
|
Chris@0
|
222 (i + levels/4, levels + levels/4, AudioLevel::PreviewLevel);
|
Chris@0
|
223 if (levels == 1) m /= 100; // noise
|
Chris@0
|
224 ll.push_back(m);
|
Chris@0
|
225 }
|
Chris@0
|
226 }
|
Chris@0
|
227 return ll;
|
Chris@0
|
228 }
|
Chris@0
|
229
|
Chris@0
|
230 int
|
Chris@0
|
231 AudioLevel::multiplier_to_preview(float m, int levels)
|
Chris@0
|
232 {
|
Chris@0
|
233 assert(levels > 0);
|
Chris@0
|
234 if (m < 0.0) return -multiplier_to_preview(-m, levels);
|
Chris@0
|
235
|
Chris@0
|
236 const LevelList &ll = getPreviewLevelCache(levels);
|
Chris@0
|
237 int result = -1;
|
Chris@0
|
238
|
Chris@0
|
239 int lo = 0, hi = levels;
|
Chris@0
|
240
|
Chris@0
|
241 // binary search
|
Chris@0
|
242 int level = -1;
|
Chris@0
|
243 while (result < 0) {
|
Chris@0
|
244 int newlevel = (lo + hi) / 2;
|
Chris@0
|
245 if (newlevel == level ||
|
Chris@0
|
246 newlevel == 0 ||
|
Chris@0
|
247 newlevel == levels) {
|
Chris@0
|
248 result = newlevel;
|
Chris@0
|
249 break;
|
Chris@0
|
250 }
|
Chris@0
|
251 level = newlevel;
|
Chris@0
|
252 if (ll[level] >= m) {
|
Chris@0
|
253 hi = level;
|
Chris@0
|
254 } else if (ll[level+1] >= m) {
|
Chris@0
|
255 result = level;
|
Chris@0
|
256 } else {
|
Chris@0
|
257 lo = level;
|
Chris@0
|
258 }
|
Chris@0
|
259 }
|
Chris@0
|
260
|
Chris@0
|
261 return result;
|
Chris@0
|
262 }
|
Chris@0
|
263
|
Chris@0
|
264 float
|
Chris@0
|
265 AudioLevel::preview_to_multiplier(int level, int levels)
|
Chris@0
|
266 {
|
Chris@0
|
267 assert(levels > 0);
|
Chris@0
|
268 if (level < 0) return -preview_to_multiplier(-level, levels);
|
Chris@0
|
269 const LevelList &ll = getPreviewLevelCache(levels);
|
Chris@0
|
270 return ll[level];
|
Chris@0
|
271 }
|
Chris@0
|
272
|
Chris@0
|
273
|