lbajardsilogic@0: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ lbajardsilogic@0: lbajardsilogic@0: /* lbajardsilogic@0: Sonic Visualiser lbajardsilogic@0: An audio file viewer and annotation editor. lbajardsilogic@0: Centre for Digital Music, Queen Mary, University of London. lbajardsilogic@0: lbajardsilogic@0: This program is free software; you can redistribute it and/or lbajardsilogic@0: modify it under the terms of the GNU General Public License as lbajardsilogic@0: published by the Free Software Foundation; either version 2 of the lbajardsilogic@0: License, or (at your option) any later version. See the file lbajardsilogic@0: COPYING included with this distribution for more information. lbajardsilogic@0: */ lbajardsilogic@0: lbajardsilogic@0: /* lbajardsilogic@0: This is a modified version of a source file from the lbajardsilogic@0: Rosegarden MIDI and audio sequencer and notation editor. lbajardsilogic@0: This file copyright 2000-2006 Chris Cannam. lbajardsilogic@0: */ lbajardsilogic@0: lbajardsilogic@0: #include "AudioLevel.h" lbajardsilogic@0: #include lbajardsilogic@0: #include lbajardsilogic@0: #include lbajardsilogic@0: #include lbajardsilogic@0: #include lbajardsilogic@0: lbajardsilogic@0: const float AudioLevel::DB_FLOOR = -1000.f; lbajardsilogic@0: lbajardsilogic@0: struct FaderDescription lbajardsilogic@0: { lbajardsilogic@0: FaderDescription(float _minDb, float _maxDb, float _zeroPoint) : lbajardsilogic@0: minDb(_minDb), maxDb(_maxDb), zeroPoint(_zeroPoint) { } lbajardsilogic@0: lbajardsilogic@0: float minDb; lbajardsilogic@0: float maxDb; lbajardsilogic@0: float zeroPoint; // as fraction of total throw lbajardsilogic@0: }; lbajardsilogic@0: lbajardsilogic@0: static const FaderDescription faderTypes[] = { lbajardsilogic@0: FaderDescription(-40.f, +6.f, 0.75f), // short lbajardsilogic@0: FaderDescription(-70.f, +10.f, 0.80f), // long lbajardsilogic@0: FaderDescription(-70.f, 0.f, 1.00f), // IEC268 lbajardsilogic@0: FaderDescription(-70.f, +10.f, 0.80f), // IEC268 long lbajardsilogic@0: FaderDescription(-40.f, 0.f, 1.00f), // preview lbajardsilogic@0: }; lbajardsilogic@0: lbajardsilogic@0: //typedef std::vector LevelList; lbajardsilogic@0: //static std::map previewLevelCache; lbajardsilogic@0: //static const LevelList &getPreviewLevelCache(int levels); lbajardsilogic@0: lbajardsilogic@0: float lbajardsilogic@0: AudioLevel::multiplier_to_dB(float multiplier) lbajardsilogic@0: { lbajardsilogic@0: if (multiplier == 0.f) return DB_FLOOR; lbajardsilogic@0: else if (multiplier < 0.f) return multiplier_to_dB(-multiplier); lbajardsilogic@0: float dB = 10 * log10f(multiplier); lbajardsilogic@0: return dB; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: float lbajardsilogic@0: AudioLevel::dB_to_multiplier(float dB) lbajardsilogic@0: { lbajardsilogic@0: if (dB == DB_FLOOR) return 0.f; lbajardsilogic@0: float m = powf(10.f, dB / 10.f); lbajardsilogic@0: return m; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: /* IEC 60-268-18 fader levels. Thanks to Steve Harris. */ lbajardsilogic@0: lbajardsilogic@0: static float iec_dB_to_fader(float db) lbajardsilogic@0: { lbajardsilogic@0: float def = 0.0f; // Meter deflection %age lbajardsilogic@0: lbajardsilogic@0: if (db < -70.0f) { lbajardsilogic@0: def = 0.0f; lbajardsilogic@0: } else if (db < -60.0f) { lbajardsilogic@0: def = (db + 70.0f) * 0.25f; lbajardsilogic@0: } else if (db < -50.0f) { lbajardsilogic@0: def = (db + 60.0f) * 0.5f + 5.0f; lbajardsilogic@0: } else if (db < -40.0f) { lbajardsilogic@0: def = (db + 50.0f) * 0.75f + 7.5f; lbajardsilogic@0: } else if (db < -30.0f) { lbajardsilogic@0: def = (db + 40.0f) * 1.5f + 15.0f; lbajardsilogic@0: } else if (db < -20.0f) { lbajardsilogic@0: def = (db + 30.0f) * 2.0f + 30.0f; lbajardsilogic@0: } else { lbajardsilogic@0: def = (db + 20.0f) * 2.5f + 50.0f; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: return def; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: static float iec_fader_to_dB(float def) // Meter deflection %age lbajardsilogic@0: { lbajardsilogic@0: float db = 0.0f; lbajardsilogic@0: lbajardsilogic@0: if (def >= 50.0f) { lbajardsilogic@0: db = (def - 50.0f) / 2.5f - 20.0f; lbajardsilogic@0: } else if (def >= 30.0f) { lbajardsilogic@0: db = (def - 30.0f) / 2.0f - 30.0f; lbajardsilogic@0: } else if (def >= 15.0f) { lbajardsilogic@0: db = (def - 15.0f) / 1.5f - 40.0f; lbajardsilogic@0: } else if (def >= 7.5f) { lbajardsilogic@0: db = (def - 7.5f) / 0.75f - 50.0f; lbajardsilogic@0: } else if (def >= 5.0f) { lbajardsilogic@0: db = (def - 5.0f) / 0.5f - 60.0f; lbajardsilogic@0: } else { lbajardsilogic@0: db = (def / 0.25f) - 70.0f; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: return db; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: float lbajardsilogic@0: AudioLevel::fader_to_dB(int level, int maxLevel, FaderType type) lbajardsilogic@0: { lbajardsilogic@0: if (level == 0) return DB_FLOOR; lbajardsilogic@0: lbajardsilogic@0: if (type == IEC268Meter || type == IEC268LongMeter) { lbajardsilogic@0: lbajardsilogic@0: float maxPercent = iec_dB_to_fader(faderTypes[type].maxDb); lbajardsilogic@0: float percent = float(level) * maxPercent / float(maxLevel); lbajardsilogic@0: float dB = iec_fader_to_dB(percent); lbajardsilogic@0: return dB; lbajardsilogic@0: lbajardsilogic@0: } else { // scale proportional to sqrt(fabs(dB)) lbajardsilogic@0: lbajardsilogic@0: int zeroLevel = int(maxLevel * faderTypes[type].zeroPoint); lbajardsilogic@0: lbajardsilogic@0: if (level >= zeroLevel) { lbajardsilogic@0: lbajardsilogic@0: float value = level - zeroLevel; lbajardsilogic@0: float scale = float(maxLevel - zeroLevel) / lbajardsilogic@0: sqrtf(faderTypes[type].maxDb); lbajardsilogic@0: value /= scale; lbajardsilogic@0: float dB = powf(value, 2.f); lbajardsilogic@0: return dB; lbajardsilogic@0: lbajardsilogic@0: } else { lbajardsilogic@0: lbajardsilogic@0: float value = zeroLevel - level; lbajardsilogic@0: float scale = zeroLevel / sqrtf(0.f - faderTypes[type].minDb); lbajardsilogic@0: value /= scale; lbajardsilogic@0: float dB = powf(value, 2.f); lbajardsilogic@0: return 0.f - dB; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: lbajardsilogic@0: int lbajardsilogic@0: AudioLevel::dB_to_fader(float dB, int maxLevel, FaderType type) lbajardsilogic@0: { lbajardsilogic@0: if (dB == DB_FLOOR) return 0; lbajardsilogic@0: lbajardsilogic@0: if (type == IEC268Meter || type == IEC268LongMeter) { lbajardsilogic@0: lbajardsilogic@0: // The IEC scale gives a "percentage travel" for a given dB lbajardsilogic@0: // level, but it reaches 100% at 0dB. So we want to treat the lbajardsilogic@0: // result not as a percentage, but as a scale between 0 and lbajardsilogic@0: // whatever the "percentage" for our (possibly >0dB) max dB is. lbajardsilogic@0: lbajardsilogic@0: float maxPercent = iec_dB_to_fader(faderTypes[type].maxDb); lbajardsilogic@0: float percent = iec_dB_to_fader(dB); lbajardsilogic@0: int faderLevel = int((maxLevel * percent) / maxPercent + 0.01f); lbajardsilogic@0: lbajardsilogic@0: if (faderLevel < 0) faderLevel = 0; lbajardsilogic@0: if (faderLevel > maxLevel) faderLevel = maxLevel; lbajardsilogic@0: return faderLevel; lbajardsilogic@0: lbajardsilogic@0: } else { lbajardsilogic@0: lbajardsilogic@0: int zeroLevel = int(maxLevel * faderTypes[type].zeroPoint); lbajardsilogic@0: lbajardsilogic@0: if (dB >= 0.f) { lbajardsilogic@0: lbajardsilogic@0: if (faderTypes[type].maxDb <= 0.f) { lbajardsilogic@0: lbajardsilogic@0: return maxLevel; lbajardsilogic@0: lbajardsilogic@0: } else { lbajardsilogic@0: lbajardsilogic@0: float value = sqrtf(dB); lbajardsilogic@0: float scale = (maxLevel - zeroLevel) / sqrtf(faderTypes[type].maxDb); lbajardsilogic@0: value *= scale; lbajardsilogic@0: int level = int(value + 0.01f) + zeroLevel; lbajardsilogic@0: if (level > maxLevel) level = maxLevel; lbajardsilogic@0: return level; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: } else { lbajardsilogic@0: lbajardsilogic@0: dB = 0.f - dB; lbajardsilogic@0: float value = sqrtf(dB); lbajardsilogic@0: float scale = zeroLevel / sqrtf(0.f - faderTypes[type].minDb); lbajardsilogic@0: value *= scale; lbajardsilogic@0: int level = zeroLevel - int(value + 0.01f); lbajardsilogic@0: if (level < 0) level = 0; lbajardsilogic@0: return level; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: lbajardsilogic@0: float lbajardsilogic@0: AudioLevel::fader_to_multiplier(int level, int maxLevel, FaderType type) lbajardsilogic@0: { lbajardsilogic@0: if (level == 0) return 0.f; lbajardsilogic@0: return dB_to_multiplier(fader_to_dB(level, maxLevel, type)); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: int lbajardsilogic@0: AudioLevel::multiplier_to_fader(float multiplier, int maxLevel, FaderType type) lbajardsilogic@0: { lbajardsilogic@0: if (multiplier == 0.f) return 0; lbajardsilogic@0: float dB = multiplier_to_dB(multiplier); lbajardsilogic@0: int fader = dB_to_fader(dB, maxLevel, type); lbajardsilogic@0: return fader; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: /* lbajardsilogic@0: const LevelList & lbajardsilogic@0: getPreviewLevelCache(int levels) lbajardsilogic@0: { lbajardsilogic@0: LevelList &ll = previewLevelCache[levels]; lbajardsilogic@0: if (ll.empty()) { lbajardsilogic@0: for (int i = 0; i <= levels; ++i) { lbajardsilogic@0: float m = AudioLevel::fader_to_multiplier lbajardsilogic@0: (i + levels/4, levels + levels/4, AudioLevel::PreviewLevel); lbajardsilogic@0: if (levels == 1) m /= 100; // noise lbajardsilogic@0: ll.push_back(m); lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: return ll; lbajardsilogic@0: } lbajardsilogic@0: */ lbajardsilogic@0: lbajardsilogic@0: int lbajardsilogic@0: AudioLevel::multiplier_to_preview(float m, int levels) lbajardsilogic@0: { lbajardsilogic@0: assert(levels > 0); lbajardsilogic@0: return multiplier_to_fader(m, levels, PreviewLevel); lbajardsilogic@0: lbajardsilogic@0: /* The original multiplier_to_preview which follows is not thread-safe. lbajardsilogic@0: lbajardsilogic@0: if (m < 0.f) return -multiplier_to_preview(-m, levels); lbajardsilogic@0: lbajardsilogic@0: const LevelList &ll = getPreviewLevelCache(levels); lbajardsilogic@0: int result = -1; lbajardsilogic@0: lbajardsilogic@0: int lo = 0, hi = levels; lbajardsilogic@0: lbajardsilogic@0: // binary search lbajardsilogic@0: int level = -1; lbajardsilogic@0: while (result < 0) { lbajardsilogic@0: int newlevel = (lo + hi) / 2; lbajardsilogic@0: if (newlevel == level || lbajardsilogic@0: newlevel == 0 || lbajardsilogic@0: newlevel == levels) { lbajardsilogic@0: result = newlevel; lbajardsilogic@0: break; lbajardsilogic@0: } lbajardsilogic@0: level = newlevel; lbajardsilogic@0: if (ll[level] >= m) { lbajardsilogic@0: hi = level; lbajardsilogic@0: } else if (ll[level+1] >= m) { lbajardsilogic@0: result = level; lbajardsilogic@0: } else { lbajardsilogic@0: lo = level; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: return result; lbajardsilogic@0: lbajardsilogic@0: */ lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: float lbajardsilogic@0: AudioLevel::preview_to_multiplier(int level, int levels) lbajardsilogic@0: { lbajardsilogic@0: assert(levels > 0); lbajardsilogic@0: return fader_to_multiplier(level, levels, PreviewLevel); lbajardsilogic@0: /* lbajardsilogic@0: if (level < 0) return -preview_to_multiplier(-level, levels); lbajardsilogic@0: const LevelList &ll = getPreviewLevelCache(levels); lbajardsilogic@0: return ll[level]; lbajardsilogic@0: */ lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: