| 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 |