from collections import namedtuple import pacmd class SinkSource: def __init__(self, pitem, kind_of_thing): assert type(pitem) == pacmd.Item self._i = pitem self._kind_of_thing = kind_of_thing def kind(self): return self._kind_of_thing def name(self): return self._i.ch["name"].value def description(self): return self._i.ch["properties"].ch["device.description"].value def default(self): return self._i.default def index(self): return self._i.index def state(self): return self._i.ch["state"].value def muted(self): return _parse_muted(self._i) def volume(self): return _parse_volume(self._i) def set_volume(self, vol): assert type(vol) == float or type(vol) == int vol = max(0.0, min(1.0, vol)) vol = round(vol * _get_maxvol(self._i)) self.set_raw_volume(vol) def set_raw_volume(self, vol): assert type(vol) == float or type(vol) == int pacmd.pacmd("set-{}-volume".format(self._kind_of_thing), str(self.index()), str(vol)) def set_muted(self, yes): pacmd.pacmd("set-{}-mute".format(self._kind_of_thing), str(self.index()), "true" if yes else "false") def set_default(self): pacmd.pacmd("set-default-{}".format(self._kind_of_thing), str(self.index())) class Sink(SinkSource): def __init__(self, pitem): super().__init__(pitem, "sink") class Source(SinkSource): def __init__(self, pitem): super().__init__(pitem, "source") class InputOutput: def __init__(self, pitem, kind_of_thing, linked_kind): assert type(pitem) == pacmd.Item self._i = pitem self._kind_of_thing = kind_of_thing self._linked_kind = linked_kind # sink-input or source-output def kind(self): return self._kind_of_thing # sink or source def linked_kind(self): return self._linked_kind def properties(self): return self._i def name(self): try: name = self._i.ch["properties"].ch["media.name"].value # Append the first that is present, if any for key in ["application.name", "application.process.binary"]: if key in self._i.ch["properties"].ch: name += " (" + self._i.ch["properties"].ch[key].value + ")" break except Exception as e: return "???" return name def linked_index(self): return int(self._i.ch[self._linked_kind].value.split()[0]) # Don't know if this is ever true; it wouldn't make much sense to have a # 'default' input or output. def default(self): return self._i.default def index(self): return self._i.index def driver(self): return self._i.ch["driver"].value def muted(self): return _parse_muted(self._i) def volume(self): return _parse_volume(self._i) def set_volume(self, vol): assert type(vol) == float or type(vol) == int vol = max(0.0, min(1.0, vol)) vol = round(vol * _get_maxvol(self._i)) self.set_raw_volume(vol) def set_raw_volume(self, vol): assert type(vol) == float or type(vol) == int pacmd.pacmd("set-{}-volume".format(self._kind_of_thing), str(self.index()), str(vol)) def set_muted(self, yes): pacmd.pacmd("set-{}-mute".format(self._kind_of_thing), str(self.index()), "true" if yes else "false") def move_to(self, idx): assert type(idx) == int pacmd.pacmd("move-{}".format(self._kind_of_thing), str(self.index()), str(idx)) class SinkInput(InputOutput): def __init__(self, pitem): super().__init__(pitem, "sink-input", "sink") class SourceOutput(InputOutput): def __init__(self, pitem): super().__init__(pitem, "source-output", "source") # TODO: How should one really do this? This is just a collection of hacks that # accidentally works for all my devices, but will probably fail the moment you # try another. def _get_maxvol(item): if "volume steps" in item.ch: return int(item.ch["volume steps"].value.strip()) - 1 elif "base volume" in item.ch: num, perc = item.ch["base volume"].value.split("/")[0:2] num = int(num.strip()) perc = int(num.replace("%", "").strip()) return num * (100 / perc) else: return 65536 def _parse_volume(item): vol = [int(x.split(":")[1].split("/")[0].strip()) for x in item.ch["volume"].value[0].split(",")] maxvol = _get_maxvol(item) return [v / maxvol for v in vol] def _parse_muted(item): if item.ch["muted"].value == "yes": return True elif item.ch["muted"].value == "no": return False else: assert False def _list_things(pacmd_obj, section_name, klass): assert len(pacmd_obj.sections) == 1 assert pacmd_obj.sections[0].name == section_name return [klass(item) for item in pacmd_obj.sections[0].items] def list_sinks(): return _list_things(pacmd.list_sinks(), "sink", Sink) def list_sink_inputs(): return _list_things(pacmd.list_sink_inputs(), "input", SinkInput) def list_sources(): return _list_things(pacmd.list_sources(), "source", Source) def list_source_outputs(): return _list_things(pacmd.list_source_outputs(), "output", SourceOutput)