annotate base/AudioLevel.cpp @ 1025:88b54a185a0a

Use double instead of float for frequencies in Pitch, just for confidence
author Chris Cannam
date Mon, 08 Dec 2014 15:37:12 +0000
parents 5bfc4930588d
children cc27f35aa75c
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@240 29 const float AudioLevel::DB_FLOOR = -1000.f;
Chris@0 30
Chris@0 31 struct FaderDescription
Chris@0 32 {
Chris@0 33 FaderDescription(float _minDb, float _maxDb, float _zeroPoint) :
Chris@0 34 minDb(_minDb), maxDb(_maxDb), zeroPoint(_zeroPoint) { }
Chris@0 35
Chris@0 36 float minDb;
Chris@0 37 float maxDb;
Chris@0 38 float zeroPoint; // as fraction of total throw
Chris@0 39 };
Chris@0 40
Chris@0 41 static const FaderDescription faderTypes[] = {
Chris@240 42 FaderDescription(-40.f, +6.f, 0.75f), // short
Chris@240 43 FaderDescription(-70.f, +10.f, 0.80f), // long
Chris@240 44 FaderDescription(-70.f, 0.f, 1.00f), // IEC268
Chris@240 45 FaderDescription(-70.f, +10.f, 0.80f), // IEC268 long
Chris@240 46 FaderDescription(-40.f, 0.f, 1.00f), // preview
Chris@0 47 };
Chris@0 48
Chris@259 49 //typedef std::vector<float> LevelList;
Chris@259 50 //static std::map<int, LevelList> previewLevelCache;
Chris@259 51 //static const LevelList &getPreviewLevelCache(int levels);
Chris@0 52
Chris@0 53 float
Chris@0 54 AudioLevel::multiplier_to_dB(float multiplier)
Chris@0 55 {
Chris@234 56 if (multiplier == 0.f) return DB_FLOOR;
Chris@234 57 else if (multiplier < 0.f) return multiplier_to_dB(-multiplier);
Chris@0 58 float dB = 10 * log10f(multiplier);
Chris@0 59 return dB;
Chris@0 60 }
Chris@0 61
Chris@0 62 float
Chris@0 63 AudioLevel::dB_to_multiplier(float dB)
Chris@0 64 {
Chris@240 65 if (dB == DB_FLOOR) return 0.f;
Chris@240 66 float m = powf(10.f, dB / 10.f);
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@0 72 static float iec_dB_to_fader(float db)
Chris@0 73 {
Chris@0 74 float 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@0 95 static float iec_fader_to_dB(float def) // Meter deflection %age
Chris@0 96 {
Chris@0 97 float 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@0 116 float
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@0 123 float maxPercent = iec_dB_to_fader(faderTypes[type].maxDb);
Chris@0 124 float percent = float(level) * maxPercent / float(maxLevel);
Chris@0 125 float 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@0 134 float value = level - zeroLevel;
Chris@0 135 float scale = float(maxLevel - zeroLevel) /
Chris@0 136 sqrtf(faderTypes[type].maxDb);
Chris@0 137 value /= scale;
Chris@240 138 float dB = powf(value, 2.f);
Chris@0 139 return dB;
Chris@0 140
Chris@0 141 } else {
Chris@0 142
Chris@0 143 float value = zeroLevel - level;
Chris@240 144 float scale = zeroLevel / sqrtf(0.f - faderTypes[type].minDb);
Chris@0 145 value /= scale;
Chris@240 146 float dB = powf(value, 2.f);
Chris@240 147 return 0.f - dB;
Chris@0 148 }
Chris@0 149 }
Chris@0 150 }
Chris@0 151
Chris@0 152
Chris@0 153 int
Chris@0 154 AudioLevel::dB_to_fader(float 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@0 165 float maxPercent = iec_dB_to_fader(faderTypes[type].maxDb);
Chris@0 166 float 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@240 177 if (dB >= 0.f) {
Chris@0 178
Chris@240 179 if (faderTypes[type].maxDb <= 0.f) {
Chris@240 180
Chris@240 181 return maxLevel;
Chris@240 182
Chris@240 183 } else {
Chris@240 184
Chris@240 185 float value = sqrtf(dB);
Chris@240 186 float scale = (maxLevel - zeroLevel) / sqrtf(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@240 195 dB = 0.f - dB;
Chris@0 196 float value = sqrtf(dB);
Chris@240 197 float scale = zeroLevel / sqrtf(0.f - 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@0 207 float
Chris@0 208 AudioLevel::fader_to_multiplier(int level, int maxLevel, FaderType type)
Chris@0 209 {
Chris@240 210 if (level == 0) return 0.f;
Chris@0 211 return dB_to_multiplier(fader_to_dB(level, maxLevel, type));
Chris@0 212 }
Chris@0 213
Chris@0 214 int
Chris@0 215 AudioLevel::multiplier_to_fader(float multiplier, int maxLevel, FaderType type)
Chris@0 216 {
Chris@240 217 if (multiplier == 0.f) return 0;
Chris@0 218 float 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@0 230 float 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@0 241 AudioLevel::multiplier_to_preview(float 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@240 248 if (m < 0.f) 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@0 280 float
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