From 4164b11eba48ca15e41b6a98f792da57ac49c65f Mon Sep 17 00:00:00 2001 From: enkore Date: Mon, 29 Jul 2013 20:15:17 +0200 Subject: [PATCH] 3.18: Add pulseaudio module (drop-in for alsa) --- README.md | 18 ++++++++-- i3pystatus/pulseaudio.py | 75 ++++++++++++++++++++++++++++++++++++++++ setup.py | 2 +- 3 files changed, 92 insertions(+), 3 deletions(-) create mode 100644 i3pystatus/pulseaudio.py diff --git a/README.md b/README.md index e0be4ae..28e0f7a 100644 --- a/README.md +++ b/README.md @@ -112,7 +112,7 @@ __Settings:__ * `alert_format_body` — (default: `Battery {battery_ident} has only {percentage:.2f}% ({remaining_hm}) remaining!`) * `alert_percentage` — (default: `10`) * `path` — (default: `None`) -* `status` — A dictionary mapping ('DIS', 'CHR', 'FULL') to alternative names (default: `{'CHR': 'CHR', 'DIS': 'DIS', 'FULL': 'FULL'}`) +* `status` — A dictionary mapping ('DIS', 'CHR', 'FULL') to alternative names (default: `{'DIS': 'DIS', 'CHR': 'CHR', 'FULL': 'FULL'}`) @@ -296,7 +296,7 @@ __Settings:__ * `host` — (default: `localhost`) * `port` — MPD port (default: `6600`) * `format` — (default: `{title} {status}`) -* `status` — Dictionary mapping pause, play and stop to output (default: `{'stop': '◾', 'play': '▶', 'pause': '▷'}`) +* `status` — Dictionary mapping pause, play and stop to output (default: `{'stop': '◾', 'pause': '▷', 'play': '▶'}`) @@ -344,6 +344,20 @@ __Settings:__ +### pulseaudio + + +Shows volume of default PulseAudio sink (output). + +Requires libpulseaudio from PyPI. Based on http://freshfoo.com/blog/pulseaudio_monitoring + + +__Settings:__ + +* `format` — {volume} is the current volume (default: `♪: {volume}`) + + + ### pyload diff --git a/i3pystatus/pulseaudio.py b/i3pystatus/pulseaudio.py new file mode 100644 index 0000000..bb9ae68 --- /dev/null +++ b/i3pystatus/pulseaudio.py @@ -0,0 +1,75 @@ +# libpulseaudio in PyPI +from pulseaudio.lib_pulseaudio import * + +from i3pystatus import Module + +class PulseAudio(Module): + """ + Shows volume of default PulseAudio sink (output). + + Requires libpulseaudio from PyPI. Based on http://freshfoo.com/blog/pulseaudio_monitoring + """ + + settings = ( + ("format", "{volume} is the current volume"), + ) + + format = "♪: {volume}" + + def init(self): + # Wrap callback methods in appropriate ctypefunc instances so + # that the Pulseaudio C API can call them + self._context_notify_cb = pa_context_notify_cb_t(self.context_notify_cb) + self._sink_info_cb = pa_sink_info_cb_t(self.sink_info_cb) + self._update_cb = pa_context_subscribe_cb_t(self.update_cb) + self._success_cb = pa_context_success_cb_t (self.success_cb) + self._server_info_cb = pa_server_info_cb_t(self.server_info_cb) + + # Create the mainloop thread and set our context_notify_cb + # method to be called when there's updates relating to the + # connection to Pulseaudio + _mainloop = pa_threaded_mainloop_new() + _mainloop_api = pa_threaded_mainloop_get_api(_mainloop) + context = pa_context_new(_mainloop_api, 'peak_demo'.encode("ascii")) + + pa_context_set_state_callback(context, self._context_notify_cb, None) + pa_context_connect(context, None, 0, None) + pa_threaded_mainloop_start(_mainloop) + + def request_update(self, context): + pa_operation_unref(pa_context_get_sink_info_by_name(context, self.sink, self._sink_info_cb, None)) + + def success_cb(self, context, success, userdata): + pass + + def server_info_cb(self, context, server_info_p, userdata): + server_info = server_info_p.contents + + self.sink = server_info.default_sink_name + + self.request_update(context) + + def context_notify_cb(self, context, _): + state = pa_context_get_state(context) + + if state == PA_CONTEXT_READY: + pa_operation_unref(pa_context_get_server_info(context, self._server_info_cb, None)) + + pa_context_set_subscribe_callback(context, self._update_cb, None) + + # PA_SUBSCRIPTION_EVENT_CHANGE + # PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT + pa_operation_unref(pa_context_subscribe(context, PA_SUBSCRIPTION_EVENT_CHANGE|PA_SUBSCRIPTION_MASK_SINK, self._success_cb, None)) + + def update_cb(self, context, t, idx, userdata): + """A sink property changed""" + self.request_update(context) + + def sink_info_cb(self, context, sink_info_p, _, __): + if sink_info_p: + sink_info = sink_info_p.contents + volume_percent = int(100 * sink_info.volume.values[0]/0x10000) + + self.output = { + "full_text": self.format.format(volume=volume_percent), + } diff --git a/setup.py b/setup.py index dd67cf4..69bf7c2 100755 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ from setuptools import setup setup(name="i3pystatus", - version="3.17", + version="3.18", description="Like i3status, this generates status line for i3bar / i3wm", url="http://github.com/enkore/i3pystatus", license="MIT",