annotate base/AudioLevel.cpp @ 1752:6d09d68165a4 by-id

Further review of ById: make IDs only available when adding a model to the ById store, not by querying the item directly. This means any id encountered in the wild must have been added to the store at some point (even if later released), which simplifies reasoning about lifecycles
author Chris Cannam
date Fri, 05 Jul 2019 15:28:07 +0100
parents 71202259002d
children 3db9a9fc2612
rev   line source
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@150 21 #include "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@573 27 #include "system/System.h"
Chris@0 28
Chris@1038 29 const double AudioLevel::DB_FLOOR = -1000.;
Chris@0 30
Chris@0 31 struct FaderDescription
Chris@0 32 {
Chris@1038 33 FaderDescription(double _minDb, double _maxDb, double _zeroPoint) :
Chris@1429 34 minDb(_minDb), maxDb(_maxDb), zeroPoint(_zeroPoint) { }
Chris@0 35
Chris@1038 36 double minDb;
Chris@1038 37 double maxDb;
Chris@1038 38 double zeroPoint; // as fraction of total throw
Chris@0 39 };
Chris@0 40
Chris@0 41 static const FaderDescription faderTypes[] = {
Chris@1038 42 FaderDescription(-40., +6., 0.75), // short
Chris@1038 43 FaderDescription(-70., +10., 0.80), // long
Chris@1038 44 FaderDescription(-70., 0., 1.00), // IEC268
Chris@1038 45 FaderDescription(-70., +10., 0.80), // IEC268 long
Chris@1038 46 FaderDescription(-40., 0., 1.00), // preview
Chris@0 47 };
Chris@0 48
Chris@1038 49 //typedef std::vector<double> LevelList;
Chris@259 50 //static std::map<int, LevelList> previewLevelCache;
Chris@259 51 //static const LevelList &getPreviewLevelCache(int levels);
Chris@0 52
Chris@1038 53 double
Chris@1038 54 AudioLevel::multiplier_to_dB(double multiplier)
Chris@0 55 {
Chris@1038 56 if (multiplier == 0.) return DB_FLOOR;
Chris@1038 57 else if (multiplier < 0.) return multiplier_to_dB(-multiplier);
Chris@1038 58 double dB = 10 * log10(multiplier);
Chris@0 59 return dB;
Chris@0 60 }
Chris@0 61
Chris@1038 62 double
Chris@1038 63 AudioLevel::dB_to_multiplier(double dB)
Chris@0 64 {
Chris@1038 65 if (dB == DB_FLOOR) return 0.;
Chris@1038 66 double m = pow(10., dB / 10.);
Chris@0 67 return m;
Chris@0 68 }
Chris@0 69
Chris@0 70 /* IEC 60-268-18 fader levels. Thanks to Steve Harris. */
Chris@0 71
Chris@1038 72 static double iec_dB_to_fader(double db)
Chris@0 73 {
Chris@1038 74 double def = 0.0f; // Meter deflection %age
Chris@0 75
Chris@0 76 if (db < -70.0f) {
Chris@0 77 def = 0.0f;
Chris@0 78 } else if (db < -60.0f) {
Chris@0 79 def = (db + 70.0f) * 0.25f;
Chris@0 80 } else if (db < -50.0f) {
Chris@750 81 def = (db + 60.0f) * 0.5f + 2.5f; // corrected from 5.0f base, thanks Robin Gareus
Chris@0 82 } else if (db < -40.0f) {
Chris@0 83 def = (db + 50.0f) * 0.75f + 7.5f;
Chris@0 84 } else if (db < -30.0f) {
Chris@0 85 def = (db + 40.0f) * 1.5f + 15.0f;
Chris@0 86 } else if (db < -20.0f) {
Chris@0 87 def = (db + 30.0f) * 2.0f + 30.0f;
Chris@0 88 } else {
Chris@0 89 def = (db + 20.0f) * 2.5f + 50.0f;
Chris@0 90 }
Chris@0 91
Chris@0 92 return def;
Chris@0 93 }
Chris@0 94
Chris@1038 95 static double iec_fader_to_dB(double def) // Meter deflection %age
Chris@0 96 {
Chris@1038 97 double db = 0.0f;
Chris@0 98
Chris@0 99 if (def >= 50.0f) {
Chris@1429 100 db = (def - 50.0f) / 2.5f - 20.0f;
Chris@0 101 } else if (def >= 30.0f) {
Chris@1429 102 db = (def - 30.0f) / 2.0f - 30.0f;
Chris@0 103 } else if (def >= 15.0f) {
Chris@1429 104 db = (def - 15.0f) / 1.5f - 40.0f;
Chris@0 105 } else if (def >= 7.5f) {
Chris@1429 106 db = (def - 7.5f) / 0.75f - 50.0f;
Chris@750 107 } else if (def >= 2.5f) {
Chris@1429 108 db = (def - 2.5f) / 0.5f - 60.0f;
Chris@0 109 } else {
Chris@1429 110 db = (def / 0.25f) - 70.0f;
Chris@0 111 }
Chris@0 112
Chris@0 113 return db;
Chris@0 114 }
Chris@0 115
Chris@1038 116 double
Chris@0 117 AudioLevel::fader_to_dB(int level, int maxLevel, FaderType type)
Chris@0 118 {
Chris@0 119 if (level == 0) return DB_FLOOR;
Chris@0 120
Chris@0 121 if (type == IEC268Meter || type == IEC268LongMeter) {
Chris@0 122
Chris@1429 123 double maxPercent = iec_dB_to_fader(faderTypes[type].maxDb);
Chris@1429 124 double percent = double(level) * maxPercent / double(maxLevel);
Chris@1429 125 double dB = iec_fader_to_dB(percent);
Chris@1429 126 return dB;
Chris@0 127
Chris@0 128 } else { // scale proportional to sqrt(fabs(dB))
Chris@0 129
Chris@1487 130 int zeroLevel = int(round(maxLevel * faderTypes[type].zeroPoint));
Chris@0 131
Chris@1429 132 if (level >= zeroLevel) {
Chris@1429 133
Chris@1429 134 double value = level - zeroLevel;
Chris@1429 135 double scale = (maxLevel - zeroLevel) /
Chris@1429 136 sqrt(faderTypes[type].maxDb);
Chris@1429 137 value /= scale;
Chris@1429 138 double dB = pow(value, 2.);
Chris@1429 139 return dB;
Chris@1429 140
Chris@1429 141 } else {
Chris@1429 142
Chris@1429 143 double value = zeroLevel - level;
Chris@1429 144 double scale = zeroLevel / sqrt(0. - faderTypes[type].minDb);
Chris@1429 145 value /= scale;
Chris@1429 146 double dB = pow(value, 2.);
Chris@1429 147 return 0. - dB;
Chris@1429 148 }
Chris@0 149 }
Chris@0 150 }
Chris@0 151
Chris@0 152
Chris@0 153 int
Chris@1038 154 AudioLevel::dB_to_fader(double dB, int maxLevel, FaderType type)
Chris@0 155 {
Chris@0 156 if (dB == DB_FLOOR) return 0;
Chris@0 157
Chris@0 158 if (type == IEC268Meter || type == IEC268LongMeter) {
Chris@0 159
Chris@1429 160 // The IEC scale gives a "percentage travel" for a given dB
Chris@1429 161 // level, but it reaches 100% at 0dB. So we want to treat the
Chris@1429 162 // result not as a percentage, but as a scale between 0 and
Chris@1429 163 // whatever the "percentage" for our (possibly >0dB) max dB is.
Chris@1429 164
Chris@1429 165 double maxPercent = iec_dB_to_fader(faderTypes[type].maxDb);
Chris@1429 166 double percent = iec_dB_to_fader(dB);
Chris@1429 167 int faderLevel = int((maxLevel * percent) / maxPercent + 0.01f);
Chris@1429 168
Chris@1429 169 if (faderLevel < 0) faderLevel = 0;
Chris@1429 170 if (faderLevel > maxLevel) faderLevel = maxLevel;
Chris@1429 171 return faderLevel;
Chris@0 172
Chris@0 173 } else {
Chris@0 174
Chris@1487 175 int zeroLevel = int(round(maxLevel * faderTypes[type].zeroPoint));
Chris@0 176
Chris@1429 177 if (dB >= 0.) {
Chris@1429 178
Chris@1038 179 if (faderTypes[type].maxDb <= 0.) {
Chris@240 180
Chris@240 181 return maxLevel;
Chris@240 182
Chris@240 183 } else {
Chris@240 184
Chris@1038 185 double value = sqrt(dB);
Chris@1038 186 double scale = (maxLevel - zeroLevel) / sqrt(faderTypes[type].maxDb);
Chris@240 187 value *= scale;
Chris@240 188 int level = int(value + 0.01f) + zeroLevel;
Chris@240 189 if (level > maxLevel) level = maxLevel;
Chris@240 190 return level;
Chris@240 191 }
Chris@1429 192
Chris@1429 193 } else {
Chris@0 194
Chris@1429 195 dB = 0. - dB;
Chris@1429 196 double value = sqrt(dB);
Chris@1429 197 double scale = zeroLevel / sqrt(0. - faderTypes[type].minDb);
Chris@1429 198 value *= scale;
Chris@1429 199 int level = zeroLevel - int(value + 0.01f);
Chris@1429 200 if (level < 0) level = 0;
Chris@1429 201 return level;
Chris@1429 202 }
Chris@0 203 }
Chris@0 204 }
Chris@0 205
Chris@1429 206
Chris@1038 207 double
Chris@0 208 AudioLevel::fader_to_multiplier(int level, int maxLevel, FaderType type)
Chris@0 209 {
Chris@1038 210 if (level == 0) return 0.;
Chris@0 211 return dB_to_multiplier(fader_to_dB(level, maxLevel, type));
Chris@0 212 }
Chris@0 213
Chris@0 214 int
Chris@1038 215 AudioLevel::multiplier_to_fader(double multiplier, int maxLevel, FaderType type)
Chris@0 216 {
Chris@1038 217 if (multiplier == 0.) return 0;
Chris@1038 218 double dB = multiplier_to_dB(multiplier);
Chris@0 219 int fader = dB_to_fader(dB, maxLevel, type);
Chris@0 220 return fader;
Chris@0 221 }
Chris@0 222
Chris@259 223 /*
Chris@0 224 const LevelList &
Chris@0 225 getPreviewLevelCache(int levels)
Chris@0 226 {
Chris@0 227 LevelList &ll = previewLevelCache[levels];
Chris@0 228 if (ll.empty()) {
Chris@1429 229 for (int i = 0; i <= levels; ++i) {
Chris@1429 230 double m = AudioLevel::fader_to_multiplier
Chris@1429 231 (i + levels/4, levels + levels/4, AudioLevel::PreviewLevel);
Chris@1429 232 if (levels == 1) m /= 100; // noise
Chris@1429 233 ll.push_back(m);
Chris@1429 234 }
Chris@0 235 }
Chris@0 236 return ll;
Chris@0 237 }
Chris@259 238 */
Chris@0 239
Chris@0 240 int
Chris@1038 241 AudioLevel::multiplier_to_preview(double m, int levels)
Chris@0 242 {
Chris@0 243 assert(levels > 0);
Chris@234 244 return multiplier_to_fader(m, levels, PreviewLevel);
Chris@234 245
Chris@234 246 /* The original multiplier_to_preview which follows is not thread-safe.
Chris@234 247
Chris@1038 248 if (m < 0.) return -multiplier_to_preview(-m, levels);
Chris@0 249
Chris@0 250 const LevelList &ll = getPreviewLevelCache(levels);
Chris@0 251 int result = -1;
Chris@0 252
Chris@0 253 int lo = 0, hi = levels;
Chris@0 254
Chris@0 255 // binary search
Chris@0 256 int level = -1;
Chris@0 257 while (result < 0) {
Chris@1429 258 int newlevel = (lo + hi) / 2;
Chris@1429 259 if (newlevel == level ||
Chris@1429 260 newlevel == 0 ||
Chris@1429 261 newlevel == levels) {
Chris@1429 262 result = newlevel;
Chris@1429 263 break;
Chris@1429 264 }
Chris@1429 265 level = newlevel;
Chris@1429 266 if (ll[level] >= m) {
Chris@1429 267 hi = level;
Chris@1429 268 } else if (ll[level+1] >= m) {
Chris@1429 269 result = level;
Chris@1429 270 } else {
Chris@1429 271 lo = level;
Chris@1429 272 }
Chris@0 273 }
Chris@1429 274
Chris@0 275 return result;
Chris@234 276
Chris@234 277 */
Chris@0 278 }
Chris@0 279
Chris@1038 280 double
Chris@0 281 AudioLevel::preview_to_multiplier(int level, int levels)
Chris@0 282 {
Chris@0 283 assert(levels > 0);
Chris@234 284 return fader_to_multiplier(level, levels, PreviewLevel);
Chris@234 285 /*
Chris@0 286 if (level < 0) return -preview_to_multiplier(-level, levels);
Chris@0 287 const LevelList &ll = getPreviewLevelCache(levels);
Chris@0 288 return ll[level];
Chris@234 289 */
Chris@0 290 }
Chris@1429 291
Chris@0 292