annotate base/AudioLevel.cpp @ 1412:b7a9edee85e0 scale-ticks

Change loop to something that feels more correct, though it makes no difference to the tests here. More tests, one failing.
author Chris Cannam
date Thu, 04 May 2017 08:32:41 +0100
parents cc27f35aa75c
children 48e9f538e6e9
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@0 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@0 100 db = (def - 50.0f) / 2.5f - 20.0f;
Chris@0 101 } else if (def >= 30.0f) {
Chris@0 102 db = (def - 30.0f) / 2.0f - 30.0f;
Chris@0 103 } else if (def >= 15.0f) {
Chris@0 104 db = (def - 15.0f) / 1.5f - 40.0f;
Chris@0 105 } else if (def >= 7.5f) {
Chris@0 106 db = (def - 7.5f) / 0.75f - 50.0f;
Chris@750 107 } else if (def >= 2.5f) {
Chris@750 108 db = (def - 2.5f) / 0.5f - 60.0f;
Chris@0 109 } else {
Chris@0 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@1038 123 double maxPercent = iec_dB_to_fader(faderTypes[type].maxDb);
Chris@1038 124 double percent = double(level) * maxPercent / double(maxLevel);
Chris@1038 125 double dB = iec_fader_to_dB(percent);
Chris@0 126 return dB;
Chris@0 127
Chris@0 128 } else { // scale proportional to sqrt(fabs(dB))
Chris@0 129
Chris@0 130 int zeroLevel = int(maxLevel * faderTypes[type].zeroPoint);
Chris@0 131
Chris@0 132 if (level >= zeroLevel) {
Chris@0 133
Chris@1038 134 double value = level - zeroLevel;
Chris@1038 135 double scale = (maxLevel - zeroLevel) /
Chris@1038 136 sqrt(faderTypes[type].maxDb);
Chris@0 137 value /= scale;
Chris@1038 138 double dB = pow(value, 2.);
Chris@0 139 return dB;
Chris@0 140
Chris@0 141 } else {
Chris@0 142
Chris@1038 143 double value = zeroLevel - level;
Chris@1038 144 double scale = zeroLevel / sqrt(0. - faderTypes[type].minDb);
Chris@0 145 value /= scale;
Chris@1038 146 double dB = pow(value, 2.);
Chris@1038 147 return 0. - dB;
Chris@0 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@0 160 // The IEC scale gives a "percentage travel" for a given dB
Chris@0 161 // level, but it reaches 100% at 0dB. So we want to treat the
Chris@0 162 // result not as a percentage, but as a scale between 0 and
Chris@0 163 // whatever the "percentage" for our (possibly >0dB) max dB is.
Chris@0 164
Chris@1038 165 double maxPercent = iec_dB_to_fader(faderTypes[type].maxDb);
Chris@1038 166 double percent = iec_dB_to_fader(dB);
Chris@240 167 int faderLevel = int((maxLevel * percent) / maxPercent + 0.01f);
Chris@0 168
Chris@0 169 if (faderLevel < 0) faderLevel = 0;
Chris@0 170 if (faderLevel > maxLevel) faderLevel = maxLevel;
Chris@0 171 return faderLevel;
Chris@0 172
Chris@0 173 } else {
Chris@0 174
Chris@0 175 int zeroLevel = int(maxLevel * faderTypes[type].zeroPoint);
Chris@0 176
Chris@1038 177 if (dB >= 0.) {
Chris@0 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@0 192
Chris@0 193 } else {
Chris@0 194
Chris@1038 195 dB = 0. - dB;
Chris@1038 196 double value = sqrt(dB);
Chris@1038 197 double scale = zeroLevel / sqrt(0. - faderTypes[type].minDb);
Chris@0 198 value *= scale;
Chris@240 199 int level = zeroLevel - int(value + 0.01f);
Chris@0 200 if (level < 0) level = 0;
Chris@0 201 return level;
Chris@0 202 }
Chris@0 203 }
Chris@0 204 }
Chris@0 205
Chris@0 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@0 229 for (int i = 0; i <= levels; ++i) {
Chris@1038 230 double m = AudioLevel::fader_to_multiplier
Chris@0 231 (i + levels/4, levels + levels/4, AudioLevel::PreviewLevel);
Chris@0 232 if (levels == 1) m /= 100; // noise
Chris@0 233 ll.push_back(m);
Chris@0 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@0 258 int newlevel = (lo + hi) / 2;
Chris@0 259 if (newlevel == level ||
Chris@0 260 newlevel == 0 ||
Chris@0 261 newlevel == levels) {
Chris@0 262 result = newlevel;
Chris@0 263 break;
Chris@0 264 }
Chris@0 265 level = newlevel;
Chris@0 266 if (ll[level] >= m) {
Chris@0 267 hi = level;
Chris@0 268 } else if (ll[level+1] >= m) {
Chris@0 269 result = level;
Chris@0 270 } else {
Chris@0 271 lo = level;
Chris@0 272 }
Chris@0 273 }
Chris@0 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@0 291
Chris@0 292