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