diff --git a/i3pystatus/alsa.py b/i3pystatus/alsa.py index f53815b..52e1180 100644 --- a/i3pystatus/alsa.py +++ b/i3pystatus/alsa.py @@ -1,4 +1,5 @@ from alsaaudio import Mixer, ALSAAudioError +from math import exp, log, log10, ceil, floor from i3pystatus import IntervalModule @@ -28,7 +29,8 @@ class ALSA(IntervalModule): ("increment", "integer percentage of max volume to in/decrement volume on mousewheel"), "muted", "unmuted", "color_muted", "color", - "channel" + "channel", + ("map_volume", "volume display/setting as in AlsaMixer. increment option is ignored then.") ) muted = "M" @@ -43,6 +45,8 @@ class ALSA(IntervalModule): channel = 0 increment = 5 + map_volume = False + alsamixer = None has_mute = True @@ -63,6 +67,11 @@ class ALSA(IntervalModule): "mixer": self.mixer, } + self.dbRng = self.alsamixer.getrange() + + self.dbMin = self.dbRng[0] + self.dbMax = self.dbRng[1] + def create_mixer(self): self.alsamixer = Mixer( control=self.mixer, id=self.mixer_id, cardindex=self.card) @@ -74,8 +83,9 @@ class ALSA(IntervalModule): if self.has_mute: muted = self.alsamixer.getmute()[self.channel] == 1 - self.fdict["volume"] = self.alsamixer.getvolume()[self.channel] + self.fdict["volume"] = self.get_cur_volume() self.fdict["muted"] = self.muted if muted else self.unmuted + self.fdict["db"] = self.get_db() if muted and self.format_muted is not None: output_format = self.format_muted @@ -92,10 +102,75 @@ class ALSA(IntervalModule): muted = self.alsamixer.getmute()[self.channel] self.alsamixer.setmute(not muted) + def get_cur_volume(self): + if self.map_volume: + dbCur = self.get_db() * 100.0 + dbMin = self.dbMin * 100.0 + dbMax = self.dbMax * 100.0 + + dbCur_norm = self.exp10((dbCur - dbMax) / 6000.0) + dbMin_norm = self.exp10((dbMin - dbMax) / 6000.0) + + vol = (dbCur_norm - dbMin_norm) / (1 - dbMin_norm) + vol = int(round(vol * 100,0)) + + return vol + else: + return self.alsamixer.getvolume()[self.channel] + + def get_new_volume(self, direction): + if direction == "inc": + volume = (self.fdict["volume"] + 1) / 100 + elif direction == "dec": + volume = (self.fdict["volume"] - 1) / 100 + + dbMin = self.dbMin * 100 + dbMax = self.dbMax * 100 + + dbMin_norm = self.exp10((dbMin - dbMax) / 6000.0) + + vol = volume * (1 - dbMin_norm) + dbMin_norm + + if direction == "inc": + dbNew = min(self.dbMax, ceil(((6000.0 * log10(vol)) + dbMax) / 100)) + elif direction == "dec": + dbNew = max(self.dbMin, floor(((6000.0 * log10(vol)) + dbMax) / 100)) + + volNew = int(round(self.map_db(dbNew, self.dbMin, self.dbMax, 0, 100),0)) + + return volNew + def increase_volume(self, delta=None): - vol = self.alsamixer.getvolume()[self.channel] - self.alsamixer.setvolume(min(100, vol + (delta if delta else self.increment))) + if self.map_volume: + vol = self.get_new_volume("inc") + + self.alsamixer.setvolume(vol) + else: + vol = self.alsamixer.getvolume()[self.channel] + self.alsamixer.setvolume(min(100, vol + (delta if delta else self.increment))) def decrease_volume(self, delta=None): - vol = self.alsamixer.getvolume()[self.channel] - self.alsamixer.setvolume(max(0, vol - (delta if delta else self.increment))) + if self.map_volume: + vol = self.get_new_volume("dec") + + self.alsamixer.setvolume(vol) + else: + vol = self.alsamixer.getvolume()[self.channel] + self.alsamixer.setvolume(max(0, vol - (delta if delta else self.increment))) + + def get_db(self): + db = (((self.dbMax - self.dbMin) / 100) * self.alsamixer.getvolume()[self.channel]) + self.dbMin + db = int(round(db,0)) + + return db + + def map_db(self, value, dbMin, dbMax, volMin, volMax): + dbRange = dbMax - dbMin + volRange = volMax - volMin + + volScaled = float(value - dbMin) / float(dbRange) + + return volMin + (volScaled * volRange) + + def exp10(self, x): + return exp(x * log(10))