from alsaaudio import Mixer, ALSAAudioError
from math import exp, log, log10, ceil, floor
from i3pystatus import IntervalModule
[docs]class ALSA(IntervalModule):
"""
Shows volume of ALSA mixer. You can also use this for inputs, btw.
Requires pyalsaaudio
.. rubric:: Available formatters
* `{volume}` — the current volume in percent
* `{muted}` — the value of one of the `muted` or `unmuted` settings
* `{card}` — the associated soundcard
* `{mixer}` — the associated ALSA mixer
"""
interval = 1
settings = (
"format",
("format_muted", "optional format string to use when muted"),
("mixer", "ALSA mixer"),
("mixer_id", "ALSA mixer id"),
("card", "ALSA sound card"),
("increment", "integer percentage of max volume to in/decrement volume on mousewheel"),
"muted", "unmuted",
"color_muted", "color",
"channel",
("map_volume", "volume display/setting as in AlsaMixer. increment option is ignored then.")
)
muted = "M"
unmuted = ""
color_muted = "#AAAAAA"
color = "#FFFFFF"
format = "♪: {volume}"
format_muted = None
mixer = "Master"
mixer_id = 0
card = 0
channel = 0
increment = 5
map_volume = False
alsamixer = None
has_mute = True
on_upscroll = "increase_volume"
on_downscroll = "decrease_volume"
on_leftclick = "switch_mute"
on_rightclick = on_leftclick
def init(self):
self.create_mixer()
try:
self.alsamixer.getmute()
except ALSAAudioError:
self.has_mute = False
self.fdict = {
"card": self.alsamixer.cardname(),
"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)
def run(self):
self.create_mixer()
muted = False
if self.has_mute:
muted = self.alsamixer.getmute()[self.channel] == 1
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
else:
output_format = self.format
self.data = self.fdict
self.output = {
"full_text": output_format.format(**self.fdict),
"color": self.color_muted if muted else self.color,
}
def switch_mute(self):
if self.has_mute:
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):
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):
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))