summaryrefslogtreecommitdiffstats
path: root/src/base/AudioLevel.cpp
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-03-01 18:37:05 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-03-01 18:37:05 +0000
commit145364a8af6a1fec06556221e66d4b724a62fc9a (patch)
tree53bd71a544008c518034f208d64c932dc2883f50 /src/base/AudioLevel.cpp
downloadrosegarden-145364a8af6a1fec06556221e66d4b724a62fc9a.tar.gz
rosegarden-145364a8af6a1fec06556221e66d4b724a62fc9a.zip
Added old abandoned KDE3 version of the RoseGarden MIDI tool
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/rosegarden@1097595 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'src/base/AudioLevel.cpp')
-rw-r--r--src/base/AudioLevel.cpp272
1 files changed, 272 insertions, 0 deletions
diff --git a/src/base/AudioLevel.cpp b/src/base/AudioLevel.cpp
new file mode 100644
index 0000000..6772c97
--- /dev/null
+++ b/src/base/AudioLevel.cpp
@@ -0,0 +1,272 @@
+// -*- c-basic-offset: 4 -*-
+
+/*
+ Rosegarden
+ A sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral right of the authors to claim authorship of this work
+ has been asserted.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#include "AudioLevel.h"
+#include <cmath>
+#include <iostream>
+#include <map>
+#include <vector>
+
+namespace Rosegarden {
+
+const float AudioLevel::DB_FLOOR = -1000.0;
+
+struct FaderDescription
+{
+ FaderDescription(float _minDb, float _maxDb, float _zeroPoint) :
+ minDb(_minDb), maxDb(_maxDb), zeroPoint(_zeroPoint) { }
+
+ float minDb;
+ float maxDb;
+ float zeroPoint; // as fraction of total throw
+};
+
+static const FaderDescription faderTypes[] = {
+ FaderDescription(-40.0, +6.0, 0.75), // short
+ FaderDescription(-70.0, +10.0, 0.80), // long
+ FaderDescription(-70.0, 0.0, 1.00), // IEC268
+ FaderDescription(-70.0, +10.0, 0.80), // IEC268 long
+ FaderDescription(-40.0, 0.0, 1.00), // preview
+};
+
+typedef std::vector<float> LevelList;
+static std::map<int, LevelList> previewLevelCache;
+static const LevelList &getPreviewLevelCache(int levels);
+
+float
+AudioLevel::multiplier_to_dB(float multiplier)
+{
+ if (multiplier == 0.0) return DB_FLOOR;
+ float dB = 10 * log10f(multiplier);
+ return dB;
+}
+
+float
+AudioLevel::dB_to_multiplier(float dB)
+{
+ if (dB == DB_FLOOR) return 0.0;
+ float m = powf(10.0, dB / 10.0);
+ return m;
+}
+
+/* IEC 60-268-18 fader levels. Thanks to Steve Harris. */
+
+static float iec_dB_to_fader(float db)
+{
+ float def = 0.0f; // Meter deflection %age
+
+ if (db < -70.0f) {
+ def = 0.0f;
+ } else if (db < -60.0f) {
+ def = (db + 70.0f) * 0.25f;
+ } else if (db < -50.0f) {
+ def = (db + 60.0f) * 0.5f + 5.0f;
+ } else if (db < -40.0f) {
+ def = (db + 50.0f) * 0.75f + 7.5f;
+ } else if (db < -30.0f) {
+ def = (db + 40.0f) * 1.5f + 15.0f;
+ } else if (db < -20.0f) {
+ def = (db + 30.0f) * 2.0f + 30.0f;
+ } else {
+ def = (db + 20.0f) * 2.5f + 50.0f;
+ }
+
+ return def;
+}
+
+static float iec_fader_to_dB(float def) // Meter deflection %age
+{
+ float db = 0.0f;
+
+ if (def >= 50.0f) {
+ db = (def - 50.0f) / 2.5f - 20.0f;
+ } else if (def >= 30.0f) {
+ db = (def - 30.0f) / 2.0f - 30.0f;
+ } else if (def >= 15.0f) {
+ db = (def - 15.0f) / 1.5f - 40.0f;
+ } else if (def >= 7.5f) {
+ db = (def - 7.5f) / 0.75f - 50.0f;
+ } else if (def >= 5.0f) {
+ db = (def - 5.0f) / 0.5f - 60.0f;
+ } else {
+ db = (def / 0.25f) - 70.0f;
+ }
+
+ return db;
+}
+
+float
+AudioLevel::fader_to_dB(int level, int maxLevel, FaderType type)
+{
+ if (level == 0) return DB_FLOOR;
+
+ if (type == IEC268Meter || type == IEC268LongMeter) {
+
+ float maxPercent = iec_dB_to_fader(faderTypes[type].maxDb);
+ float percent = float(level) * maxPercent / float(maxLevel);
+ float dB = iec_fader_to_dB(percent);
+ return dB;
+
+ } else { // scale proportional to sqrt(fabs(dB))
+
+ int zeroLevel = int(maxLevel * faderTypes[type].zeroPoint);
+
+ if (level >= zeroLevel) {
+
+ float value = level - zeroLevel;
+ float scale = float(maxLevel - zeroLevel) /
+ sqrtf(faderTypes[type].maxDb);
+ value /= scale;
+ float dB = powf(value, 2.0);
+ return dB;
+
+ } else {
+
+ float value = zeroLevel - level;
+ float scale = zeroLevel / sqrtf(0.0 - faderTypes[type].minDb);
+ value /= scale;
+ float dB = powf(value, 2.0);
+ return 0.0 - dB;
+ }
+ }
+}
+
+
+int
+AudioLevel::dB_to_fader(float dB, int maxLevel, FaderType type)
+{
+ if (dB == DB_FLOOR) return 0;
+
+ if (type == IEC268Meter || type == IEC268LongMeter) {
+
+ // The IEC scale gives a "percentage travel" for a given dB
+ // level, but it reaches 100% at 0dB. So we want to treat the
+ // result not as a percentage, but as a scale between 0 and
+ // whatever the "percentage" for our (possibly >0dB) max dB is.
+
+ float maxPercent = iec_dB_to_fader(faderTypes[type].maxDb);
+ float percent = iec_dB_to_fader(dB);
+ int faderLevel = int((maxLevel * percent) / maxPercent + 0.01);
+
+ if (faderLevel < 0) faderLevel = 0;
+ if (faderLevel > maxLevel) faderLevel = maxLevel;
+ return faderLevel;
+
+ } else {
+
+ int zeroLevel = int(maxLevel * faderTypes[type].zeroPoint);
+
+ if (dB >= 0.0) {
+
+ float value = sqrtf(dB);
+ float scale = (maxLevel - zeroLevel) / sqrtf(faderTypes[type].maxDb);
+ value *= scale;
+ int level = int(value + 0.01) + zeroLevel;
+ if (level > maxLevel) level = maxLevel;
+ return level;
+
+ } else {
+
+ dB = 0.0 - dB;
+ float value = sqrtf(dB);
+ float scale = zeroLevel / sqrtf(0.0 - faderTypes[type].minDb);
+ value *= scale;
+ int level = zeroLevel - int(value + 0.01);
+ if (level < 0) level = 0;
+ return level;
+ }
+ }
+}
+
+
+float
+AudioLevel::fader_to_multiplier(int level, int maxLevel, FaderType type)
+{
+ if (level == 0) return 0.0;
+ return dB_to_multiplier(fader_to_dB(level, maxLevel, type));
+}
+
+int
+AudioLevel::multiplier_to_fader(float multiplier, int maxLevel, FaderType type)
+{
+ if (multiplier == 0.0) return 0;
+ float dB = multiplier_to_dB(multiplier);
+ int fader = dB_to_fader(dB, maxLevel, type);
+ return fader;
+}
+
+
+const LevelList &
+getPreviewLevelCache(int levels)
+{
+ LevelList &ll = previewLevelCache[levels];
+ if (ll.empty()) {
+ for (int i = 0; i <= levels; ++i) {
+ float m = AudioLevel::fader_to_multiplier
+ (i, levels, AudioLevel::PreviewLevel);
+ if (levels == 1) m /= 100; // noise
+ ll.push_back(m);
+ }
+ }
+ return ll;
+}
+
+int
+AudioLevel::multiplier_to_preview(float m, int levels)
+{
+ const LevelList &ll = getPreviewLevelCache(levels);
+ int result = -1;
+
+ int lo = 0, hi = levels;
+
+ // binary search
+ int level = -1;
+ while (result < 0) {
+ int newlevel = (lo + hi) / 2;
+ if (newlevel == level ||
+ newlevel == 0 ||
+ newlevel == levels) {
+ result = newlevel;
+ break;
+ }
+ level = newlevel;
+ if (ll[level] >= m) {
+ hi = level;
+ } else if (ll[level+1] >= m) {
+ result = level;
+ } else {
+ lo = level;
+ }
+ }
+
+ return result;
+}
+
+float
+AudioLevel::preview_to_multiplier(int level, int levels)
+{
+ const LevelList &ll = getPreviewLevelCache(levels);
+ return ll[level];
+}
+
+
+}
+