annotate base/AudioLevel.cpp @ 876:47aa3aeb687b tonioni

For outputs with unknown bin count or multiple bins with variable sample rate, create additional output models for bins beyond the first
author Chris Cannam
date Wed, 29 Jan 2014 09:31:22 +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