"""MultiMixer, a sensor for controlling an array based display with multiplechannels available.by Justin "nagash" Jones <randomgalssuck@iinet.net.au>Credit goes to Martin Aspeli <optilude@gmx.net>, I used alot of his stuff.This is really just a modified version of his Mixer sensor, with support forarrays and hence, more than one channel.Thanks also go to the people who made StarterBar and SysInfo. I studied theirdesklets while figuring out how to use arrays. :)There is a couple of bugs with gdesklets <= 0.25.1pycage tells me they will be fixed for 0.26. They arent major, just a couple ofproblems with arrays.Also, this sensor is quite cpu intensive, especially when u start adding more than2 or 3 mixer channels. You have been warned.Parameters...- width: the width of your gauges, this is needed to calculate for the "click to set volume"- increment: the default amount (in %) you wish to set for "scroll to change volume"- device: the mixer device- interval: the update time (in ms). anything lower than 200 is a little too cpu intensive for my liking. anything greater makes it feel lagged when u change the volume- fontnumber: the number of fonts u wish to use (ala FontSelector Sensor)- fontargs: ala FontSelectorThe only thing from Psi-Disp-Themes which this doesnt support is the gauges, due to thearrays. I was going to add the gauge setting into this sensor, but I dont think it's necessary.I'm almost positive my code isnt the nicest in the world. If someone wants to clean it up,be my guest. :)"""from sensor.Sensor import Sensorfrom utils.datatypes import *import ossaudiodevclass MultiMixer(Sensor): CHANNELS = [ [ "Volume", "Vol", ossaudiodev.SOUND_MIXER_VOLUME ], [ "PCM", "PCM", ossaudiodev.SOUND_MIXER_PCM ], [ "Bass", "Bass", ossaudiodev.SOUND_MIXER_BASS ], [ "Treble", "Treb", ossaudiodev.SOUND_MIXER_BASS ], [ "Synth", "Synth", ossaudiodev.SOUND_MIXER_SYNTH ], [ "PC Speaker", "PCS", ossaudiodev.SOUND_MIXER_SPEAKER ], [ "Line 1", "Line1", ossaudiodev.SOUND_MIXER_LINE1 ], [ "Line 2", "Line2", ossaudiodev.SOUND_MIXER_LINE2 ], [ "Line 3", "Line3", ossaudiodev.SOUND_MIXER_LINE3 ], [ "Line Out", "LiOut", ossaudiodev.SOUND_MIXER_LINE ], [ "Microphone", "Mic", ossaudiodev.SOUND_MIXER_MIC ], [ "CD Audio", "CDA", ossaudiodev.SOUND_MIXER_CD ], [ "Input Gain", "IGain", ossaudiodev.SOUND_MIXER_IGAIN ], [ "Output Gain", "OGain", ossaudiodev.SOUND_MIXER_OGAIN ], [ "Digital 1", "Dig1", ossaudiodev.SOUND_MIXER_DIGITAL1 ], [ "Digital 2", "Dig2", ossaudiodev.SOUND_MIXER_DIGITAL2 ], [ "Digital 3", "Dig3", ossaudiodev.SOUND_MIXER_DIGITAL3 ], [ "Phone In", "PhIn", ossaudiodev.SOUND_MIXER_PHONEIN ], [ "Phone Out", "PhOut", ossaudiodev.SOUND_MIXER_PHONEOUT ], [ "Video", "Video", ossaudiodev.SOUND_MIXER_VIDEO ], [ "Radio", "Radio", ossaudiodev.SOUND_MIXER_RADIO ], [ "Monitor", "Mon", ossaudiodev.SOUND_MIXER_MONITOR ] ] AVAILABLE_CHANNELS = [] def __init__(self, width, increment = 5, device = "/dev/dsp", interval = 200, fontnumber = 1, *fontargs): self.__xpos = 0 self.__width = int(width) self.__interval = int(interval) self.__activeChannels = [] self.__size = 0 self.__size_changed = 0 self.__fontnumber = int(fontnumber) self.__fonts = [] self.__colors = [] Sensor.__init__(self) # Setup basic configuration entries self._set_config_type("device", TYPE_STRING, device) self._set_config_type("increment", TYPE_INT, int(increment)) # Setup font configuration entries if (len(fontargs) == 1 and fontargs[0].find(" ") != -1): fontargs = (fontargs[0].replace(" ", ",").replace(" ", ",")).split(",") print "Using spaces to separate font entries is deprecated." print "Use commas instead." fontargs = list(fontargs) for font_n in xrange(self.__fontnumber): if (fontargs): font = fontargs.pop(0) else: font = "Sans 8" if (fontargs): color = fontargs.pop(0) else: color = "black" self._set_config_type("font%i" % font_n, TYPE_STRING, font) self._set_config_type("color%i" % font_n, TYPE_STRING, color) # Setup channel configuration entries mixer = ossaudiodev.openmixer(device) i = 0 for channel in self.CHANNELS: if mixer.controls() & (1 << channel[2]): self.AVAILABLE_CHANNELS.append([ channel[0], channel[1], channel[2] ]) self._set_config_type(channel[1], TYPE_BOOL, 0) i += 1 mixer.close() self._watch_config(self.__on_watch_config) self._add_timer(0, self.__channel_init) self._add_timer(0, self.__font_init) self._add_timer(0, self.__get_data) def __channel_init(self): output = self._new_output() for channel in self.AVAILABLE_CHANNELS: if self._get_config(channel[1]) == 1: self.__size += 1 self.__size_changed = 1 self.__activeChannels.append([ channel[1], channel[2] ]) output = self._new_output() output.set("size", self.__size) self._send_output(output) def __get_data(self): output = self._new_output() output.set("device", self._get_config("device")) index = 0 for channel in self.__activeChannels: output.set("channel[%d]" % index, channel[0]) output.set("volume[%d]" % index, self._get_volume(channel[1])) index += 1 self._add_timer(self.__interval, self.__get_data) self._send_output(output) def __font_init(self): fonts = [] colors = [] for font_n in xrange(self.__fontnumber): fonts.append(self._get_config("font%i" % font_n)) colors.append(self._get_config("color%i" % font_n)) output = self._new_output() for i in range(self.__size): for font_n in xrange(self.__fontnumber): output.set("font%i[%d]" % (font_n, i), fonts[font_n]) output.set("color%i[%d]" % (font_n, i), colors[font_n]) self._send_output(output) def get_configurator(self): configurator = self._new_configurator() configurator.set_name("Mixer") # Basic Configuration configurator.add_title("Audio Mixer") configurator.add_entry("Device", "device", "Mixer Device") configurator.add_spin("Wheel Increment", "increment", "Percent to increase/decrease", 1, 10) # Fonts configuration configurator.add_title("Fonts") for font_n in xrange(self.__fontnumber): configurator.add_font_selector("Font #%i:" % font_n, "font%i" % font_n, "Font #%i used in the labels" % font_n) configurator.add_color_selector("Font color #%i:" % font_n, "color%i" % font_n, "Font color #%i used in the labels" % font_n) # Mixer Configuration configurator.add_title("Mixer Channels") for channel in self.AVAILABLE_CHANNELS: configurator.add_checkbox(channel[0] + " (" + channel[1] + ")", channel[1], "Enable this mixer channel?") return configurator def __on_watch_config(self, key, value): for channel in self.AVAILABLE_CHANNELS: if (key == channel[1]): if value == "false": self.__size -= 1 self.__size_changed = 1 self.__activeChannels.remove([ channel[1], channel[2] ]) self._add_timer(self.__interval + 50, self.__font_init) elif value == "true": self.__size += 1 self.__size_changed = 1 self.__activeChannels.append([ channel[1], channel[2] ]) self._add_timer(self.__interval + 50, self.__font_init) output = self._new_output() output.set("size", self.__size) self._send_output(output) def call_action(self, action, path, args = []): if action == "move": self.__xpos = args[0] elif action == "click": index = path[-1] volume = (self.__xpos * 100) / self.__width volume = ((self.__xpos - 45) * 100) / self.__width self._set_volume(volume, index) elif action == "scroll": increment = 0 if args[0] == 0: # Volume up increment = self._get_config("increment") elif args[0] == 1: # Volume down increment = self._get_config("increment") * -1 if increment != 0: index = path[-1] self._change_volume(increment, index) def _get_mixer(self): device = self._get_config("device") mixer = None try: mixer = ossaudiodev.openmixer(device) if not mixer: raise "Error opening mixer" except: return None return mixer def _get_volume(self, channel = None): mixer = None try: mixer = self._get_mixer() if mixer and channel >= 0: volumes = mixer.get(channel) volume = (volumes[0] + volumes[1]) / 2 return volume else: return -1 finally: if mixer: mixer.close() def _get_channel(self, index): channel = None i = 0 for value in self.AVAILABLE_CHANNELS: if self._get_config(value[1]) == 1: if i == index: channel = value[2] i += 1 return channel def _set_volume (self, volume, index): "Set the volume to the given value (0-100)" mixer = None channel = self._get_channel(index) try: mixer = self._get_mixer() if mixer and channel >= 0: mixer.set(channel, (volume, volume)) finally: if mixer: mixer.close() def _change_volume (self, increment, index): "Change the volume by increment (may be negative), but stay within range 0-100" mixer = None channel = self._get_channel(index) try: mixer = self._get_mixer() if mixer and channel >= 0: volumes = mixer.get(channel) volume = (volumes[0] + volumes[1])/2 new_volume = volume + increment if new_volume < 0: new_volume = 0 elif new_volume > 100: new_volume = 100 mixer.set(channel, (new_volume, new_volume)) finally: if mixer: mixer.close()def new_sensor(args): return apply(MultiMixer, args)