annotate base/AudioLevel.cpp @ 76:af2725b5d6fe

* Implement harmonic cursor in spectrogram * Implement layer export. This doesn't quite do the right thing for the SV XML layer export yet -- it doesn't include layer display information, so when imported, it only creates an invisible model. Could also do with fixing CSV file import so as to work correctly for note and text layers.
author Chris Cannam
date Mon, 10 Apr 2006 17:22:59 +0000
parents d397ea0a79f5
children 4b2ea82fd0ed
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@0 21 #include "base/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@0 28 const float AudioLevel::DB_FLOOR = -1000.0;
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@0 41 FaderDescription(-40.0, +6.0, 0.75), // short
Chris@0 42 FaderDescription(-70.0, +10.0, 0.80), // long
Chris@0 43 FaderDescription(-70.0, 0.0, 1.00), // IEC268
Chris@0 44 FaderDescription(-70.0, +10.0, 0.80), // IEC268 long
Chris@0 45 FaderDescription(-40.0, 0.0, 1.00), // preview
Chris@0 46 };
Chris@0 47
Chris@0 48 typedef std::vector<float> LevelList;
Chris@0 49 static std::map<int, LevelList> previewLevelCache;
Chris@0 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@0 55 if (multiplier == 0.0) return DB_FLOOR;
Chris@0 56 float dB = 10 * log10f(multiplier);
Chris@0 57 return dB;
Chris@0 58 }
Chris@0 59
Chris@0 60 float
Chris@0 61 AudioLevel::dB_to_multiplier(float dB)
Chris@0 62 {
Chris@0 63 if (dB == DB_FLOOR) return 0.0;
Chris@0 64 float m = powf(10.0, dB / 10.0);
Chris@0 65 return m;
Chris@0 66 }
Chris@0 67
Chris@0 68 /* IEC 60-268-18 fader levels. Thanks to Steve Harris. */
Chris@0 69
Chris@0 70 static float iec_dB_to_fader(float db)
Chris@0 71 {
Chris@0 72 float def = 0.0f; // Meter deflection %age
Chris@0 73
Chris@0 74 if (db < -70.0f) {
Chris@0 75 def = 0.0f;
Chris@0 76 } else if (db < -60.0f) {
Chris@0 77 def = (db + 70.0f) * 0.25f;
Chris@0 78 } else if (db < -50.0f) {
Chris@0 79 def = (db + 60.0f) * 0.5f + 5.0f;
Chris@0 80 } else if (db < -40.0f) {
Chris@0 81 def = (db + 50.0f) * 0.75f + 7.5f;
Chris@0 82 } else if (db < -30.0f) {
Chris@0 83 def = (db + 40.0f) * 1.5f + 15.0f;
Chris@0 84 } else if (db < -20.0f) {
Chris@0 85 def = (db + 30.0f) * 2.0f + 30.0f;
Chris@0 86 } else {
Chris@0 87 def = (db + 20.0f) * 2.5f + 50.0f;
Chris@0 88 }
Chris@0 89
Chris@0 90 return def;
Chris@0 91 }
Chris@0 92
Chris@0 93 static float iec_fader_to_dB(float def) // Meter deflection %age
Chris@0 94 {
Chris@0 95 float db = 0.0f;
Chris@0 96
Chris@0 97 if (def >= 50.0f) {
Chris@0 98 db = (def - 50.0f) / 2.5f - 20.0f;
Chris@0 99 } else if (def >= 30.0f) {
Chris@0 100 db = (def - 30.0f) / 2.0f - 30.0f;
Chris@0 101 } else if (def >= 15.0f) {
Chris@0 102 db = (def - 15.0f) / 1.5f - 40.0f;
Chris@0 103 } else if (def >= 7.5f) {
Chris@0 104 db = (def - 7.5f) / 0.75f - 50.0f;
Chris@0 105 } else if (def >= 5.0f) {
Chris@0 106 db = (def - 5.0f) / 0.5f - 60.0f;
Chris@0 107 } else {
Chris@0 108 db = (def / 0.25f) - 70.0f;
Chris@0 109 }
Chris@0 110
Chris@0 111 return db;
Chris@0 112 }
Chris@0 113
Chris@0 114 float
Chris@0 115 AudioLevel::fader_to_dB(int level, int maxLevel, FaderType type)
Chris@0 116 {
Chris@0 117 if (level == 0) return DB_FLOOR;
Chris@0 118
Chris@0 119 if (type == IEC268Meter || type == IEC268LongMeter) {
Chris@0 120
Chris@0 121 float maxPercent = iec_dB_to_fader(faderTypes[type].maxDb);
Chris@0 122 float percent = float(level) * maxPercent / float(maxLevel);
Chris@0 123 float dB = iec_fader_to_dB(percent);
Chris@0 124 return dB;
Chris@0 125
Chris@0 126 } else { // scale proportional to sqrt(fabs(dB))
Chris@0 127
Chris@0 128 int zeroLevel = int(maxLevel * faderTypes[type].zeroPoint);
Chris@0 129
Chris@0 130 if (level >= zeroLevel) {
Chris@0 131
Chris@0 132 float value = level - zeroLevel;
Chris@0 133 float scale = float(maxLevel - zeroLevel) /
Chris@0 134 sqrtf(faderTypes[type].maxDb);
Chris@0 135 value /= scale;
Chris@0 136 float dB = powf(value, 2.0);
Chris@0 137 return dB;
Chris@0 138
Chris@0 139 } else {
Chris@0 140
Chris@0 141 float value = zeroLevel - level;
Chris@0 142 float scale = zeroLevel / sqrtf(0.0 - faderTypes[type].minDb);
Chris@0 143 value /= scale;
Chris@0 144 float dB = powf(value, 2.0);
Chris@0 145 return 0.0 - dB;
Chris@0 146 }
Chris@0 147 }
Chris@0 148 }
Chris@0 149
Chris@0 150
Chris@0 151 int
Chris@0 152 AudioLevel::dB_to_fader(float dB, int maxLevel, FaderType type)
Chris@0 153 {
Chris@0 154 if (dB == DB_FLOOR) return 0;
Chris@0 155
Chris@0 156 if (type == IEC268Meter || type == IEC268LongMeter) {
Chris@0 157
Chris@0 158 // The IEC scale gives a "percentage travel" for a given dB
Chris@0 159 // level, but it reaches 100% at 0dB. So we want to treat the
Chris@0 160 // result not as a percentage, but as a scale between 0 and
Chris@0 161 // whatever the "percentage" for our (possibly >0dB) max dB is.
Chris@0 162
Chris@0 163 float maxPercent = iec_dB_to_fader(faderTypes[type].maxDb);
Chris@0 164 float percent = iec_dB_to_fader(dB);
Chris@0 165 int faderLevel = int((maxLevel * percent) / maxPercent + 0.01);
Chris@0 166
Chris@0 167 if (faderLevel < 0) faderLevel = 0;
Chris@0 168 if (faderLevel > maxLevel) faderLevel = maxLevel;
Chris@0 169 return faderLevel;
Chris@0 170
Chris@0 171 } else {
Chris@0 172
Chris@0 173 int zeroLevel = int(maxLevel * faderTypes[type].zeroPoint);
Chris@0 174
Chris@0 175 if (dB >= 0.0) {
Chris@0 176
Chris@0 177 float value = sqrtf(dB);
Chris@0 178 float scale = (maxLevel - zeroLevel) / sqrtf(faderTypes[type].maxDb);
Chris@0 179 value *= scale;
Chris@0 180 int level = int(value + 0.01) + zeroLevel;
Chris@0 181 if (level > maxLevel) level = maxLevel;
Chris@0 182 return level;
Chris@0 183
Chris@0 184 } else {
Chris@0 185
Chris@0 186 dB = 0.0 - dB;
Chris@0 187 float value = sqrtf(dB);
Chris@0 188 float scale = zeroLevel / sqrtf(0.0 - faderTypes[type].minDb);
Chris@0 189 value *= scale;
Chris@0 190 int level = zeroLevel - int(value + 0.01);
Chris@0 191 if (level < 0) level = 0;
Chris@0 192 return level;
Chris@0 193 }
Chris@0 194 }
Chris@0 195 }
Chris@0 196
Chris@0 197
Chris@0 198 float
Chris@0 199 AudioLevel::fader_to_multiplier(int level, int maxLevel, FaderType type)
Chris@0 200 {
Chris@0 201 if (level == 0) return 0.0;
Chris@0 202 return dB_to_multiplier(fader_to_dB(level, maxLevel, type));
Chris@0 203 }
Chris@0 204
Chris@0 205 int
Chris@0 206 AudioLevel::multiplier_to_fader(float multiplier, int maxLevel, FaderType type)
Chris@0 207 {
Chris@0 208 if (multiplier == 0.0) return 0;
Chris@0 209 float dB = multiplier_to_dB(multiplier);
Chris@0 210 int fader = dB_to_fader(dB, maxLevel, type);
Chris@0 211 return fader;
Chris@0 212 }
Chris@0 213
Chris@0 214
Chris@0 215 const LevelList &
Chris@0 216 getPreviewLevelCache(int levels)
Chris@0 217 {
Chris@0 218 LevelList &ll = previewLevelCache[levels];
Chris@0 219 if (ll.empty()) {
Chris@0 220 for (int i = 0; i <= levels; ++i) {
Chris@0 221 float m = AudioLevel::fader_to_multiplier
Chris@0 222 (i + levels/4, levels + levels/4, AudioLevel::PreviewLevel);
Chris@0 223 if (levels == 1) m /= 100; // noise
Chris@0 224 ll.push_back(m);
Chris@0 225 }
Chris@0 226 }
Chris@0 227 return ll;
Chris@0 228 }
Chris@0 229
Chris@0 230 int
Chris@0 231 AudioLevel::multiplier_to_preview(float m, int levels)
Chris@0 232 {
Chris@0 233 assert(levels > 0);
Chris@0 234 if (m < 0.0) return -multiplier_to_preview(-m, levels);
Chris@0 235
Chris@0 236 const LevelList &ll = getPreviewLevelCache(levels);
Chris@0 237 int result = -1;
Chris@0 238
Chris@0 239 int lo = 0, hi = levels;
Chris@0 240
Chris@0 241 // binary search
Chris@0 242 int level = -1;
Chris@0 243 while (result < 0) {
Chris@0 244 int newlevel = (lo + hi) / 2;
Chris@0 245 if (newlevel == level ||
Chris@0 246 newlevel == 0 ||
Chris@0 247 newlevel == levels) {
Chris@0 248 result = newlevel;
Chris@0 249 break;
Chris@0 250 }
Chris@0 251 level = newlevel;
Chris@0 252 if (ll[level] >= m) {
Chris@0 253 hi = level;
Chris@0 254 } else if (ll[level+1] >= m) {
Chris@0 255 result = level;
Chris@0 256 } else {
Chris@0 257 lo = level;
Chris@0 258 }
Chris@0 259 }
Chris@0 260
Chris@0 261 return result;
Chris@0 262 }
Chris@0 263
Chris@0 264 float
Chris@0 265 AudioLevel::preview_to_multiplier(int level, int levels)
Chris@0 266 {
Chris@0 267 assert(levels > 0);
Chris@0 268 if (level < 0) return -preview_to_multiplier(-level, levels);
Chris@0 269 const LevelList &ll = getPreviewLevelCache(levels);
Chris@0 270 return ll[level];
Chris@0 271 }
Chris@0 272
Chris@0 273