annotate base/AudioLevel.cpp @ 33:51e158b505da

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