#!/usr/bin/python import pygtk, gtk, sys from ctypes import * try: libpulse = cdll.LoadLibrary("../.libs/libpulse.so") except OSError: try: libpulse = cdll.LoadLibrary(".libs/libpulse.so") except OSError: libpulse = cdll.LoadLibrary("libpulse.so") class ChannelMap(Structure): _fields_ = [("channels", c_ubyte), ("map", c_uint * 32)] _to_name = libpulse.pa_channel_map_to_name _to_name.restype = c_char_p _to_name.argtypes = [c_void_p] _to_pretty_name = libpulse.pa_channel_map_to_pretty_name _to_pretty_name.restype = c_char_p _to_pretty_name.argtypes = [c_void_p] _snprint = libpulse.pa_channel_map_snprint _snprint.restype = c_char_p _snprint.argtypes = [c_char_p, c_ulong, c_void_p] _position_to_string = libpulse.pa_channel_position_to_string _position_to_string.restype = c_char_p _position_to_string.argtypes = [c_uint] _position_to_pretty_string = libpulse.pa_channel_position_to_pretty_string _position_to_pretty_string.restype = c_char_p _position_to_pretty_string.argtypes = [c_uint] _can_balance = libpulse.pa_channel_map_can_balance _can_balance.restype = c_int _can_balance.argtypes = [c_void_p] _can_fade = libpulse.pa_channel_map_can_fade _can_fade.restype = c_int _can_fade.argtypes = [c_void_p] _parse = libpulse.pa_channel_map_parse _parse.restype = c_void_p _parse.argtypes = [c_void_p, c_char_p] def to_name(this): return this._to_name(byref(this)) def to_pretty_name(this): return this._to_pretty_name(byref(this)) def snprint(this): s = create_string_buffer(336) r = this._snprint(s, len(s), byref(this)) if r is None: return None else: return s.value def position_to_string(this, pos): return this._position_to_string(pos) def position_to_pretty_string(this, pos): return this._position_to_pretty_string(pos) def can_balance(this): return bool(this._can_balance(byref(this))) def can_fade(this): return bool(this._can_fade(byref(this))) def parse(this, s): if this._parse(byref(this), s) is None: raise Exception("Parse failure") class CVolume(Structure): _fields_ = [("channels", c_ubyte), ("values", c_uint32 * 32)] _snprint = libpulse.pa_cvolume_snprint _snprint.restype = c_char_p _snprint.argtypes = [c_char_p, c_ulong, c_void_p] _max = libpulse.pa_cvolume_max _max.restype = c_uint32 _max.argtypes = [c_void_p] _scale = libpulse.pa_cvolume_scale _scale.restype = c_void_p _scale.argtypes = [c_void_p, c_uint32] _get_balance = libpulse.pa_cvolume_get_balance _get_balance.restype = c_float _get_balance.argtypes = [c_void_p, c_void_p] _get_fade = libpulse.pa_cvolume_get_fade _get_fade.restype = c_float _get_fade.argtypes = [c_void_p, c_void_p] _set_balance = libpulse.pa_cvolume_set_balance _set_balance.restype = c_void_p _set_balance.argtypes = [c_void_p, c_void_p, c_float] _set_fade = libpulse.pa_cvolume_set_fade _set_fade.restype = c_void_p _set_fade.argtypes = [c_void_p, c_void_p, c_float] _to_dB = libpulse.pa_sw_volume_to_dB _to_dB.restype = c_double _to_dB.argytpes = [c_uint32] def snprint(this): s = create_string_buffer(320) r = this._snprint(s, len(s), byref(this)) if r is None: return None else: return s.raw def max(this): return this._max(byref(this)) def scale(this, v): return this._scale(byref(this), v) def get_balance(this, cm): return this._get_balance(byref(this), byref(cm)) def get_fade(this, cm): return this._get_fade(byref(this), byref(cm)) def set_balance(this, cm, f): return this._set_balance(byref(this), byref(cm), f) def set_fade(this, cm, f): return this._set_fade(byref(this), byref(cm), f) def to_dB(this, channel = None): if channel is None: return this._to_dB(this.max()) return this._to_dB(this.values[channel]) cm = ChannelMap() if len(sys.argv) > 1: cm.parse(sys.argv[1]) else: cm.parse("surround-51") v = CVolume() v.channels = cm.channels for i in range(cm.channels): v.values[i] = 65536 title = cm.to_pretty_name() if title is None: title = cm.snprint() window = gtk.Window(gtk.WINDOW_TOPLEVEL) window.set_title(unicode(title)) window.set_border_width(12) vbox = gtk.VBox(spacing=6) channel_labels = {} channel_scales = {} channel_dB_labels = {} def update_volume(update_channels = True, update_fade = True, update_balance = True, update_scale = True): if update_channels: for i in range(cm.channels): channel_scales[i].set_value(v.values[i]) if update_scale: value_scale.set_value(v.max()) if update_balance: balance_scale.set_value(v.get_balance(cm)) if update_fade: fade_scale.set_value(v.get_fade(cm)) for i in range(cm.channels): channel_dB_labels[i].set_label("%0.2f dB" % v.to_dB(i)) value_dB_label.set_label("%0.2f dB" % v.to_dB()) def fade_value_changed(fs): v.set_fade(cm, fade_scale.get_value()) update_volume(update_fade = False) def balance_value_changed(fs): v.set_balance(cm, balance_scale.get_value()) update_volume(update_balance = False) def value_value_changed(fs): v.scale(int(value_scale.get_value())) update_volume(update_scale = False) def channel_value_changed(fs, i): v.values[i] = int(channel_scales[i].get_value()) update_volume(update_channels = False) for i in range(cm.channels): channel_labels[i] = gtk.Label(cm.position_to_pretty_string(cm.map[i])) channel_labels[i].set_alignment(0, 1) vbox.pack_start(channel_labels[i], expand=False, fill=True) channel_scales[i] = gtk.HScale() channel_scales[i].set_range(0, 65536*3/2) channel_scales[i].set_digits(0) channel_scales[i].set_value_pos(gtk.POS_RIGHT) vbox.pack_start(channel_scales[i], expand=False, fill=True) channel_dB_labels[i] = gtk.Label("-xxx dB") channel_dB_labels[i].set_alignment(1, 1) vbox.pack_start(channel_dB_labels[i], expand=False, fill=True) value_label = gtk.Label("Value") value_label.set_alignment(0, .5) vbox.pack_start(value_label, expand=False, fill=True) value_scale = gtk.HScale() value_scale.set_range(0, 65536*3/2) value_scale.set_value_pos(gtk.POS_RIGHT) value_scale.set_digits(0) vbox.pack_start(value_scale, expand=False, fill=True) value_dB_label = gtk.Label("-xxx dB") value_dB_label.set_alignment(1, 1) vbox.pack_start(value_dB_label, expand=False, fill=True) balance_label = gtk.Label("Balance") balance_label.set_alignment(0, .5) vbox.pack_start(balance_label, expand=False, fill=True) balance_scale = gtk.HScale() balance_scale.set_range(-1.0, +1.0) balance_scale.set_value_pos(gtk.POS_RIGHT) balance_scale.set_digits(2) vbox.pack_start(balance_scale, expand=False, fill=True) fade_label = gtk.Label("Fade") fade_label.set_alignment(0, .5) vbox.pack_start(fade_label, expand=False, fill=True) fade_scale = gtk.HScale() fade_scale.set_range(-1.0, +1.0) fade_scale.set_value_pos(gtk.POS_RIGHT) fade_scale.set_digits(2) vbox.pack_start(fade_scale, expand=False, fill=True) window.add(vbox) window.set_default_size(600, 50) update_volume() for i in range(cm.channels): channel_scales[i].connect("value_changed", channel_value_changed, i) fade_scale.connect("value_changed", fade_value_changed) balance_scale.connect("value_changed", balance_value_changed) value_scale.connect("value_changed", value_value_changed) vbox.show_all() if not cm.can_balance(): balance_label.hide() balance_scale.hide() if not cm.can_fade(): fade_label.hide() fade_scale.hide() window.show() gtk.main()