annotate base/AudioLevel.cpp @ 490:c3fb8258e34d

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