1#!/usr/bin/python 2 3import pygtk, gtk, sys 4from ctypes import * 5 6try: 7 libpulse = cdll.LoadLibrary("../.libs/libpulse.so") 8except OSError: 9 try: 10 libpulse = cdll.LoadLibrary(".libs/libpulse.so") 11 except OSError: 12 libpulse = cdll.LoadLibrary("libpulse.so") 13 14class ChannelMap(Structure): 15 _fields_ = [("channels", c_ubyte), 16 ("map", c_uint * 32)] 17 18 _to_name = libpulse.pa_channel_map_to_name 19 _to_name.restype = c_char_p 20 _to_name.argtypes = [c_void_p] 21 22 _to_pretty_name = libpulse.pa_channel_map_to_pretty_name 23 _to_pretty_name.restype = c_char_p 24 _to_pretty_name.argtypes = [c_void_p] 25 26 _snprint = libpulse.pa_channel_map_snprint 27 _snprint.restype = c_char_p 28 _snprint.argtypes = [c_char_p, c_ulong, c_void_p] 29 30 _position_to_string = libpulse.pa_channel_position_to_string 31 _position_to_string.restype = c_char_p 32 _position_to_string.argtypes = [c_uint] 33 34 _position_to_pretty_string = libpulse.pa_channel_position_to_pretty_string 35 _position_to_pretty_string.restype = c_char_p 36 _position_to_pretty_string.argtypes = [c_uint] 37 38 _can_balance = libpulse.pa_channel_map_can_balance 39 _can_balance.restype = c_int 40 _can_balance.argtypes = [c_void_p] 41 42 _can_fade = libpulse.pa_channel_map_can_fade 43 _can_fade.restype = c_int 44 _can_fade.argtypes = [c_void_p] 45 46 _parse = libpulse.pa_channel_map_parse 47 _parse.restype = c_void_p 48 _parse.argtypes = [c_void_p, c_char_p] 49 50 def to_name(this): 51 return this._to_name(byref(this)) 52 53 def to_pretty_name(this): 54 return this._to_pretty_name(byref(this)) 55 56 def snprint(this): 57 s = create_string_buffer(336) 58 r = this._snprint(s, len(s), byref(this)) 59 60 if r is None: 61 return None 62 else: 63 return s.value 64 65 def position_to_string(this, pos): 66 return this._position_to_string(pos) 67 68 def position_to_pretty_string(this, pos): 69 return this._position_to_pretty_string(pos) 70 71 def can_balance(this): 72 return bool(this._can_balance(byref(this))) 73 74 def can_fade(this): 75 return bool(this._can_fade(byref(this))) 76 77 def parse(this, s): 78 if this._parse(byref(this), s) is None: 79 raise Exception("Parse failure") 80 81 82class CVolume(Structure): 83 _fields_ = [("channels", c_ubyte), 84 ("values", c_uint32 * 32)] 85 86 _snprint = libpulse.pa_cvolume_snprint 87 _snprint.restype = c_char_p 88 _snprint.argtypes = [c_char_p, c_ulong, c_void_p] 89 90 _max = libpulse.pa_cvolume_max 91 _max.restype = c_uint32 92 _max.argtypes = [c_void_p] 93 94 _scale = libpulse.pa_cvolume_scale 95 _scale.restype = c_void_p 96 _scale.argtypes = [c_void_p, c_uint32] 97 98 _get_balance = libpulse.pa_cvolume_get_balance 99 _get_balance.restype = c_float 100 _get_balance.argtypes = [c_void_p, c_void_p] 101 102 _get_fade = libpulse.pa_cvolume_get_fade 103 _get_fade.restype = c_float 104 _get_fade.argtypes = [c_void_p, c_void_p] 105 106 _set_balance = libpulse.pa_cvolume_set_balance 107 _set_balance.restype = c_void_p 108 _set_balance.argtypes = [c_void_p, c_void_p, c_float] 109 110 _set_fade = libpulse.pa_cvolume_set_fade 111 _set_fade.restype = c_void_p 112 _set_fade.argtypes = [c_void_p, c_void_p, c_float] 113 114 _to_dB = libpulse.pa_sw_volume_to_dB 115 _to_dB.restype = c_double 116 _to_dB.argytpes = [c_uint32] 117 118 def snprint(this): 119 s = create_string_buffer(320) 120 r = this._snprint(s, len(s), byref(this)) 121 122 if r is None: 123 return None 124 else: 125 return s.raw 126 127 def max(this): 128 return this._max(byref(this)) 129 130 def scale(this, v): 131 return this._scale(byref(this), v) 132 133 def get_balance(this, cm): 134 return this._get_balance(byref(this), byref(cm)) 135 136 def get_fade(this, cm): 137 return this._get_fade(byref(this), byref(cm)) 138 139 def set_balance(this, cm, f): 140 return this._set_balance(byref(this), byref(cm), f) 141 142 def set_fade(this, cm, f): 143 return this._set_fade(byref(this), byref(cm), f) 144 145 def to_dB(this, channel = None): 146 if channel is None: 147 return this._to_dB(this.max()) 148 149 return this._to_dB(this.values[channel]) 150 151cm = ChannelMap() 152 153if len(sys.argv) > 1: 154 cm.parse(sys.argv[1]) 155else: 156 cm.parse("surround-51") 157 158v = CVolume() 159v.channels = cm.channels 160 161for i in range(cm.channels): 162 v.values[i] = 65536 163 164title = cm.to_pretty_name() 165if title is None: 166 title = cm.snprint() 167 168window = gtk.Window(gtk.WINDOW_TOPLEVEL) 169window.set_title(unicode(title)) 170window.set_border_width(12) 171 172vbox = gtk.VBox(spacing=6) 173 174channel_labels = {} 175channel_scales = {} 176channel_dB_labels = {} 177 178def update_volume(update_channels = True, update_fade = True, update_balance = True, update_scale = True): 179 if update_channels: 180 for i in range(cm.channels): 181 channel_scales[i].set_value(v.values[i]) 182 183 if update_scale: 184 value_scale.set_value(v.max()) 185 186 if update_balance: 187 balance_scale.set_value(v.get_balance(cm)) 188 189 if update_fade: 190 fade_scale.set_value(v.get_fade(cm)) 191 192 for i in range(cm.channels): 193 channel_dB_labels[i].set_label("%0.2f dB" % v.to_dB(i)) 194 195 value_dB_label.set_label("%0.2f dB" % v.to_dB()) 196 197def fade_value_changed(fs): 198 v.set_fade(cm, fade_scale.get_value()) 199 update_volume(update_fade = False) 200 201def balance_value_changed(fs): 202 v.set_balance(cm, balance_scale.get_value()) 203 update_volume(update_balance = False) 204 205def value_value_changed(fs): 206 v.scale(int(value_scale.get_value())) 207 update_volume(update_scale = False) 208 209def channel_value_changed(fs, i): 210 v.values[i] = int(channel_scales[i].get_value()) 211 update_volume(update_channels = False) 212 213for i in range(cm.channels): 214 channel_labels[i] = gtk.Label(cm.position_to_pretty_string(cm.map[i])) 215 channel_labels[i].set_alignment(0, 1) 216 vbox.pack_start(channel_labels[i], expand=False, fill=True) 217 218 channel_scales[i] = gtk.HScale() 219 channel_scales[i].set_range(0, 65536*3/2) 220 channel_scales[i].set_digits(0) 221 channel_scales[i].set_value_pos(gtk.POS_RIGHT) 222 vbox.pack_start(channel_scales[i], expand=False, fill=True) 223 224 channel_dB_labels[i] = gtk.Label("-xxx dB") 225 channel_dB_labels[i].set_alignment(1, 1) 226 vbox.pack_start(channel_dB_labels[i], expand=False, fill=True) 227 228value_label = gtk.Label("Value") 229value_label.set_alignment(0, .5) 230vbox.pack_start(value_label, expand=False, fill=True) 231value_scale = gtk.HScale() 232value_scale.set_range(0, 65536*3/2) 233value_scale.set_value_pos(gtk.POS_RIGHT) 234value_scale.set_digits(0) 235vbox.pack_start(value_scale, expand=False, fill=True) 236value_dB_label = gtk.Label("-xxx dB") 237value_dB_label.set_alignment(1, 1) 238vbox.pack_start(value_dB_label, expand=False, fill=True) 239 240balance_label = gtk.Label("Balance") 241balance_label.set_alignment(0, .5) 242vbox.pack_start(balance_label, expand=False, fill=True) 243balance_scale = gtk.HScale() 244balance_scale.set_range(-1.0, +1.0) 245balance_scale.set_value_pos(gtk.POS_RIGHT) 246balance_scale.set_digits(2) 247vbox.pack_start(balance_scale, expand=False, fill=True) 248 249fade_label = gtk.Label("Fade") 250fade_label.set_alignment(0, .5) 251vbox.pack_start(fade_label, expand=False, fill=True) 252fade_scale = gtk.HScale() 253fade_scale.set_range(-1.0, +1.0) 254fade_scale.set_value_pos(gtk.POS_RIGHT) 255fade_scale.set_digits(2) 256vbox.pack_start(fade_scale, expand=False, fill=True) 257 258window.add(vbox) 259window.set_default_size(600, 50) 260 261update_volume() 262 263for i in range(cm.channels): 264 channel_scales[i].connect("value_changed", channel_value_changed, i) 265fade_scale.connect("value_changed", fade_value_changed) 266balance_scale.connect("value_changed", balance_value_changed) 267value_scale.connect("value_changed", value_value_changed) 268 269vbox.show_all() 270 271if not cm.can_balance(): 272 balance_label.hide() 273 balance_scale.hide() 274 275if not cm.can_fade(): 276 fade_label.hide() 277 fade_scale.hide() 278 279 280window.show() 281 282gtk.main() 283