# # THIS FILE IS PART OF THE JOKOSHER PROJECT AND LICENSED UNDER THE GPL. SEE # THE 'COPYING' FILE FOR DETAILS # # InstrumentEffectsDialog.py # # This module us used to present the dialog for adding and managing instrument # effects. Note that this module deals with the main effects dialog *and* the # effect settings dialog, which we're both handed to Mr. Bacon in a spec by the # Gods of Metal. They came to him in a dream to warn against a bleak future where # massively distorted music could not be made using linux. # #------------------------------------------------------------------------------- import gtk.glade import gobject import cairo import pygst pygst.require("0.10") import gst import os import Globals import EffectPresets import gettext _ = gettext.gettext #========================================================================= class InstrumentEffectsDialog: """ This class displays and implements the Instrument effects dialog box, which can be used to add, remove, and edit effects for an Instrument. """ #_____________________________________________________________________ def __init__(self, instrument, destroyCallback, windowIcon): """ This constructor enables a lot of variables, reads in the glade file for the main dialog, and populates the effects and presets controls. Parameters: instrument -- Instrument whose effects are being modified. destroyCallback -- GTK callback. Called when this dialog gets destroyed. windowIcon -- icon to use on the window's upper corner. """ # a reference to the instrument object self.instrument = instrument self.windowIcon = windowIcon self.res = gtk.glade.XML(Globals.GLADE_PATH, "InstrumentEffectsDialog") self.Updating = False self.effectWidgets = [] self.signals = { "on_comboPresets_changed" : self.OnEffectChainPresetChanged, "on_buttonPresetSave_clicked" : self.OnEffectChainPresetSave, "on_buttonPresetDelete_clicked" : self.OnEffectChainPresetDelete, "on_comboCategories_changed" : self.OnCategoryChanged, "on_listEffects_row_activated" : self.OnEffectActivated, "on_listActiveEffects_row_activated" : self.OnActiveEffectActivated, "on_buttonEffectAdd_clicked" : self.OnEffectAdd, "on_buttonEffectUp_clicked" : self.OnEffectUp, "on_buttonEffectDown_clicked" : self.OnEffectDown, "on_buttonEffectDelete_clicked" : self.OnEffectDeleted, "on_buttonEffectSettings_clicked" : self.OnEffectSettings, "on_buttonPlay_clicked" : self.OnPlay, "on_buttonClose_clicked" : self.OnClose, "on_InstrumentEffects_configure_event" : self.OnResize, "on_InstrumentEffects_destroy" : self.OnDestroy } # auto connect the signals to the methods self.res.signal_autoconnect(self.signals) # grab references to some Glade items self.window = self.res.get_widget("InstrumentEffectsDialog") self.mainvbox = self.res.get_widget("InstrumentEffectsDialogVBox") self.imageInstrument = self.res.get_widget("imageInstrument") self.comboPresets = self.res.get_widget("comboPresets") self.buttonPresetSave = self.res.get_widget("buttonPresetSave") self.buttonPresetDelete = self.res.get_widget("buttonPresetDelete") self.comboCategories = self.res.get_widget("comboCategories") self.listEffects = self.res.get_widget("listEffects") self.buttonEffectAdd = self.res.get_widget("buttonEffectAdd") self.labelActiveEffects = self.res.get_widget("labelActiveEffects") self.listActiveEffects = self.res.get_widget("listActiveEffects") self.buttonEffectUp = self.res.get_widget("buttonEffectUp") self.buttonEffectDown = self.res.get_widget("buttonEffectDown") self.buttonEffectDelete = self.res.get_widget("buttonEffectDelete") self.buttonEffectSettings = self.res.get_widget("buttonEffectSettings") self.buttonPlay = self.res.get_widget("buttonPlay") self.buttonClose = self.res.get_widget("buttonClose") # connect the right-click signal for both treeviews self.listEffects.connect("button-press-event", self.OnEffectsTreeViewClick) self.listActiveEffects.connect("button-press-event", self.OnActiveEffectsTreeViewClick) # connect the destroy signal and set the window icon self.window.connect("destroy", destroyCallback) self.window.set_icon(self.windowIcon) # set single selection for the list views self.listEffects.get_selection().set_mode(gtk.SELECTION_SINGLE) self.listActiveEffects.get_selection().set_mode(gtk.SELECTION_SINGLE) # create and pack the header image self.headerCairoImage = CairoDialogHeaderImage(_("Instrument Effects")) self.headerCairoImage.set_size_request(self.window.allocation.width, 60) self.mainvbox.pack_start(self.headerCairoImage, False, False) self.headerCairoImage.show() # set the instrument name and image from self.instrument self.imageInstrument.set_from_pixbuf(self.instrument.pixbuf.scale_simple(32, 32, gtk.gdk.INTERP_BILINEAR)) self.labelActiveEffects.set_label(self.labelActiveEffects.get_label()+self.instrument.name) # create the appropriate models self.modelCategories = gtk.ListStore(gtk.gdk.Pixbuf, str) #image, category self.modelPresets = gtk.ListStore(str) #name self.modelEffects = gtk.ListStore(gtk.gdk.Pixbuf, str, str, str, bool) #image, shortname, longname, category, listed self.modelActiveEffects = gtk.ListStore(gtk.gdk.Pixbuf, str, str, int) #image, shortname, longname, uniqueID # create a model filter for the effects model, and set it's visible column self.filterEffects = self.modelEffects.filter_new() self.filterEffects.set_visible_column(4) # set the models for each widget accordingly self.comboCategories.set_model(self.modelCategories) self.comboPresets.set_model(self.modelPresets) self.listEffects.set_model(self.filterEffects) self.listActiveEffects.set_model(self.modelActiveEffects) # create the columns with their respective renderers and add them self.listEffects.append_column(gtk.TreeViewColumn(_("Name"), gtk.CellRendererText(), text=2)) self.listActiveEffects.append_column(gtk.TreeViewColumn(_("Type"), gtk.CellRendererPixbuf(), pixbuf=0)) self.listActiveEffects.append_column(gtk.TreeViewColumn(_("Name"), gtk.CellRendererText(), text=2)) # effects combo pixRenderer = gtk.CellRendererPixbuf() pixRenderer.set_property("xpad", 6) pixRenderer.set_property("ypad", 3) textRenderer = gtk.CellRendererText() self.comboCategories.clear() # all combos by default have one textRenderer. Remove it # before adding the pixRenderer. self.comboCategories.pack_start(pixRenderer, expand=False) self.comboCategories.pack_start(textRenderer) self.comboCategories.add_attribute(pixRenderer, "pixbuf", 0) self.comboCategories.add_attribute(textRenderer, "text", 1) # presets combo self.comboPresets.clear() textRenderer = gtk.CellRendererText() self.comboPresets.pack_start(textRenderer) self.comboPresets.add_attribute(textRenderer, "text", 0) # create a copy of the categories list to avoid errors in the for's categories = Globals.LADPSA_CATEGORIES_LIST #the temporary list to sort them tempEffectList = [] # load all available effects into modelEffects for effect in Globals.LADSPA_NAME_MAP: newEffect = [None, None, None, None, None] try: imageFile = categories[Globals.LADSPA_CATEGORIES_DICT[effect[0]]][1] # image path category = categories[Globals.LADSPA_CATEGORIES_DICT[effect[0]]][0] except KeyError: # unclassified imageFile = categories[1][1] category = categories[1][0] newEffect[0] = gtk.gdk.pixbuf_new_from_file(os.path.join(Globals.IMAGE_PATH, imageFile)) # image newEffect[1] = effect[0] # short name newEffect[2] = effect[1] # long name newEffect[3] = category newEffect[4] = False # listed in the left effects pane tempEffectList.append(newEffect) tempEffectList.sort(key=lambda x: x[2].lower()) #sort alphabetically by long name for newEffect in tempEffectList: self.modelEffects.append(newEffect) # create a list with available presets and populate the model self.presets = EffectPresets.EffectPresets() self._LoadInstrumentPresets() # append the categories and their image to the categories combo box for index, category in enumerate(categories): # skip the Broken category if index == 0: continue for effect in self.modelEffects: #If there is at least one effect in this category; always include unclassified if effect[3] == category[0] or index == 1: imageFile = category[1] icon = gtk.gdk.pixbuf_new_from_file(os.path.join(Globals.IMAGE_PATH, imageFile)) self.modelCategories.append([icon, category[0]]) break #set the first one active self.comboCategories.set_active(0) # this says if the project is playing, so we know to toggle the # play button in the dialog self.isPlaying = self.instrument.project.GetIsPlaying() if self.isPlaying: self.buttonPlay.set_use_stock(True) self.buttonPlay.set_label(gtk.STOCK_MEDIA_STOP) # listen to the Project, Instrument and Preset changes self.instrument.project.connect("audio-state::play", self.OnProjectPlay) self.instrument.project.connect("audio-state::record", self.OnProjectRecord) self.instrument.project.connect("audio-state::stop", self.OnProjectStop) self.instrument.connect("effect", self.OnInstrumentEffect) self.presets.connect("single-preset", self.OnSinglePreset) self.presets.connect("chain-preset", self.OnChainPreset) self.updatinggui = True if self.instrument.currentchainpreset is not None: self.comboPresets.set_active(self.instrument.currentchainpreset) self.updatinggui = False self.width = int(Globals.settings.general["instrumenteffectwindowwidth"]) self.height = int(Globals.settings.general["instrumenteffectwindowheight"]) self.window.resize(self.width, self.height) self.Update() #_____________________________________________________________________ def _LoadInstrumentPresets(self): """ Loads the presets for this Instrument type if there are any available. It then adds them to the chain presets combo box model. """ self.availpresets = [] self.modelPresets.clear() self.presets.FillEffectsPresetsRegistry() try: instrPresets = self.presets.effectpresetregistry["instruments"][self.instrument.instrType] for pres in instrPresets: if instrPresets[pres]["dependencies"].issubset(Globals.LADSPA_FACTORY_REGISTRY): self.availpresets.append(pres) self.modelPresets.append([pres]) except KeyError: # no presets for this Instrument pass #_____________________________________________________________________ def Update(self): """ Refreshes the effects inside the modelActiveEffects when they are added, removed or shuffled. """ if self.Updating: return self.Updating = True # create a copy of the categories list to avoid errors in the for categories = Globals.LADPSA_CATEGORIES_LIST # clear the active effects list self.modelActiveEffects.clear() # append all the active effects to the modelActiveEffects for effect in self.instrument.effects: shortName = effect.get_factory().get_name() longName = effect.get_factory().get_longname() activeEffect = [None, None, None, None] try: imageFile = categories[Globals.LADSPA_CATEGORIES_DICT[shortName]][1] # image path except KeyError: imageFile = categories[1][1] # unclassified activeEffect[0] = gtk.gdk.pixbuf_new_from_file(os.path.join(Globals.IMAGE_PATH, imageFile)) activeEffect[1] = shortName activeEffect[2] = longName activeEffect[3] = len(self.modelActiveEffects) self.modelActiveEffects.append(activeEffect) self.Updating = False #_____________________________________________________________________ def OnClose(self, button): """ If the OK button is pressed on the dialog box, the window is destroyed. Parameters: button -- reserved for GTK callbacks, don't use it explicitly. """ self.instrument.project.disconnect_by_func(self.OnProjectPlay) self.instrument.project.disconnect_by_func(self.OnProjectRecord) self.instrument.project.disconnect_by_func(self.OnProjectStop) self.instrument.disconnect_by_func(self.OnInstrumentEffect) self.window.destroy() #_____________________________________________________________________ def OnPlay(self, button): """ Pressing the Play/Stop button on the dialog box allows the user to play back the project to test if the effect settings are right for them. When user press the Play button, it switches to a stop button, and vice versa. Parameters: button -- reserved for GTK callbacks, don't use it explicitly. """ if self.isPlaying == False: # things to do if the project is not already playing, and hence # needs to start playing self.instrument.project.Play() self.isPlaying = True else: self.instrument.project.Stop() self.isPlaying = False #_____________________________________________________________________ def OnSinglePreset(self, effectPresets): """ Callback function for when the a the list of single effects is modified. """ self._LoadEffectPresets() #_____________________________________________________________________ def OnChainPreset(self, effectPresets): """ Callback function for when the a the list of chain effects is modified. """ self._LoadInstrumentPresets() #_____________________________________________________________________ def OnProjectRecord(self, project): """ Callback for when the project starts recording. Parameters: project -- The project instance that send the signal. """ self.window.set_sensitive(False) #_____________________________________________________________________ def OnProjectPlay(self, project): """ Callback for when the project starts playing. Parameters: project -- The project instance that send the signal. """ # things to do if the project is not already playing, and hence # needs to start playing self.buttonPlay.set_use_stock(True) self.buttonPlay.set_label(gtk.STOCK_MEDIA_STOP) # set this to True to show we are now playing self.isPlaying = True #_____________________________________________________________________ def OnProjectStop(self, project): """ Callback for when the project stops playing or recording. Parameters: project -- The project instance that send the signal. """ self.window.set_sensitive(True) # things to do when the stop button is pressed to stop playback self.buttonPlay.set_use_stock(True) self.buttonPlay.set_label(gtk.STOCK_MEDIA_PLAY) # set this to False to show we are no longer playing self.isPlaying = False #_____________________________________________________________________ def OnInstrumentEffect(self, instrument): """ Callback for when the effects on the instrument change. Parameters: instrument -- the instrument instance that send the signal. """ self.Update() #_____________________________________________________________________ def OnCategoryChanged(self, combo): """ Updates the list of available effects depending on the category chosen. Parameters: combo -- reserved for GTK callbacks, don't use it explicitly. """ # display all the correspondent effects in the effects list for effect in self.modelEffects: if effect[3] == self.modelCategories[combo.get_active()][1]: #match the category effect[4] = True #display the effect else: effect[4] = False #hide the effect #_____________________________________________________________________ def OnActiveEffectsTreeViewClick(self, widget, mouse): """ Called when the user presses a mouse button over the ListActiveEffects TreeView. If it's a right-click, creates a context menu on the fly for manipulating effects. Parameters: widget -- reserved for GTK callbacks, don't use it explicitly. mouse -- GTK mouse event that fired this method call. """ selection = self.listActiveEffects.get_selection().get_selected() # return if there is no active selection if not selection[1]: return # Create context menu on a right-click if mouse.button == 3: menu = gtk.Menu() items = [ (_("_Move up..."), self.OnEffectUp, True, gtk.image_new_from_stock(gtk.STOCK_GO_UP, gtk.ICON_SIZE_MENU)), (_("M_ove down..."), self.OnEffectDown, True, gtk.image_new_from_stock(gtk.STOCK_GO_DOWN, gtk.ICON_SIZE_MENU)), ("---", None, False, None), (_("_Delete"), self.OnEffectDeleted, True, gtk.image_new_from_stock(gtk.STOCK_DELETE, gtk.ICON_SIZE_MENU)), (_("_Settings"), self.OnEffectSettings, True, gtk.image_new_from_stock(gtk.STOCK_PROPERTIES, gtk.ICON_SIZE_MENU)) ] for label, callback, sensitivity, image in items: if label == "---": menuItem = gtk.SeparatorMenuItem() elif image: menuItem = gtk.ImageMenuItem(label, True) menuItem.set_image(image) else: menuItem = gtk.MenuItem(label=label) menuItem.set_sensitive(sensitivity) menuItem.show() menu.append(menuItem) if callback: menuItem.connect("activate", callback) menu.popup(None, None, None, mouse.button, mouse.time) #_____________________________________________________________________ def OnEffectsTreeViewClick(self, widget, mouse): """ Called when the user presses a mouse button over the ListEffects TreeView. If it's a right-click, creates a context menu on the fly for manipulating effects. Parameters: widget -- reserved for GTK callbacks, don't use it explicitly. mouse -- GTK mouse event that fired this method call. """ selection = self.listEffects.get_selection().get_selected() # return if there is no active selection if not selection[1]: return # Create context menu on a right-click if mouse.button == 3: menu = gtk.Menu() menuItem = gtk.ImageMenuItem(_("_Activate effect"), True) menuItem.set_image(gtk.image_new_from_stock(gtk.STOCK_GO_FORWARD, gtk.ICON_SIZE_MENU)) menuItem.show() menu.append(menuItem) menuItem.connect("activate", self.OnEffectAdd) menu.popup(None, None, None, mouse.button, mouse.time) #_____________________________________________________________________ def OnEffectActivated(self, treeview, path, view_column): """ Adds the double clicked effect from the left effects pane to the Instrument. Parameters: treeview -- treeview that fired this event. path -- path to the activated row. Format: (index,) view_column -- the column in the activated row. """ self.OnEffectAdd() #_____________________________________________________________________ def OnActiveEffectActivated(self, treeview, path, view_column): """ Displays the settings for the double clicked effect from the active effects pane. Parameters: treeview -- treeview that fired this event. path -- path to the activated row. Format: (index,) view_column -- the column in the activated row. """ self.OnEffectSettings() #_____________________________________________________________________ def OnEffectAdd(self, widget=None): """ Adds the currently selected effect to the Instrument. Parameters: widget -- reserved for GTK callbacks, don't use it explicitly. """ selection = self.listEffects.get_selection().get_selected() # return if there is no active selection if not selection[1]: return self.instrument.AddEffect(self.filterEffects[selection[1]][1]) #_____________________________________________________________________ def OnEffectUp(self, button): """ Moves the selected effect one position up on the list. Parameters: button -- reserved for GTK callbacks, don't use it explicitly. """ selection = self.listActiveEffects.get_selection().get_selected() # return if there is no active selection or it's the first element if not selection[1] or self.modelActiveEffects[selection[1]].path == (0,): return # grab references to the effect position in the table and the # effect element itself, to then move it effectPos = self.modelActiveEffects[selection[1]].path[0] effect = self.instrument.effects[effectPos] newPosition = self.modelActiveEffects[selection[1]].path[0]-1 self.instrument.ChangeEffectOrder(effect, newPosition) # reselect the effect moved in this operation self.listActiveEffects.set_cursor(newPosition) #_____________________________________________________________________ def OnEffectDown(self, button): """ Moves the selected effect one position down on the list. Parameters: button -- reserved for GTK callbacks, don't use it explicitly. """ selection = self.listActiveEffects.get_selection().get_selected() # return if there is no active selection or it's the last element if not selection[1] or self.modelActiveEffects[selection[1]].next == None: return # grab references to the effect position in the table and the # effect element itself, to then move it effectPos = self.modelActiveEffects[selection[1]].path[0] effect = self.instrument.effects[effectPos] newPosition = self.modelActiveEffects[selection[1]].path[0]+1 self.instrument.ChangeEffectOrder(effect, newPosition) # reselect the effect moved in this operation self.listActiveEffects.set_cursor(newPosition) #_____________________________________________________________________ def OnEffectDeleted(self, widget): """ Removes the currently selected effect from the Instrument. Parameters: widget -- reserved for GTK callbacks, don't use it explicitly. """ selection = self.listActiveEffects.get_selection().get_selected() # return if there is no active selection if not selection[1]: return # grab references to the effect position in the table and the # effect element itself, to then delete it effectPos = self.modelActiveEffects[selection[1]].path[0] effect = self.instrument.effects[effectPos] self.instrument.RemoveEffect(effect) # select another existing active effect for usability, # so users can repeteadly click on delete to delete all effects if len(self.modelActiveEffects) > 0: self.listActiveEffects.set_cursor(0) #_____________________________________________________________________ def OnEffectSettings(self, button=None): """ Show a dialog filled with settings sliders for a specific effect Parameters: button -- reserved for GTK callbacks, don't use it explicitly. """ # TODO: Make this modal or as part of the effects window selection = self.listActiveEffects.get_selection().get_selected() # return if there is no active selection if not selection[1]: return # grab references to the effect position in the table and the # effect element itself self.effectpos = self.modelActiveEffects[selection[1]].path[0] self.effectelement = self.instrument.effects[self.effectpos] # this variable is used to slash the values for the different sliders self.sliderdict = {} # Threshold value to determine if a property is buggy self.propertyThreshold = 1000000 # Min/Max value assigned to buggy properties self.fixValue = 15.0 # set the index of the current edited effect - used to reference the # effect elsewhere self.settWin = gtk.glade.XML(Globals.GLADE_PATH, "EffectSettingsDialog") settsignals = { "on_presetsCombo_changed" : self.OnSingleEffectPresetChanged, "on_savePresetButton_clicked" : self.OnSingleEffectPresetSave, "on_deletePresetButton_clicked" : self.OnSingleEffectPresetDelete, "on_closeButton_clicked" : self.OnSingleEffectSettingsClose } self.settWin.signal_autoconnect(settsignals) # create references to glade items self.settingswindow = self.settWin.get_widget("EffectSettingsDialog") self.settingsvbox = self.settWin.get_widget("EffectSettingsVBox") self.effectLabel = self.settWin.get_widget("effectLabel") self.effectImage = self.settWin.get_widget("effectImage") self.settingstable = self.settWin.get_widget("settingsTable") self.presetscombo = self.settWin.get_widget("presetsCombo") self.settingsHeaderCairoImage = CairoDialogHeaderImage(_("Effect Settings")) self.settingsHeaderCairoImage.set_size_request(450, 60) self.settingsvbox.pack_start(self.settingsHeaderCairoImage, expand=False, fill=True) self.settingsHeaderCairoImage.show() # set the window icon and parent (for correct modal mode) self.settingswindow.set_icon(self.windowIcon) self.settingswindow.set_transient_for(self.window) # grab a list of properties from the effect proplist = gobject.list_properties(self.instrument.effects[self.effectpos]) # set the effect name and icon self.effectLabel.set_label("%s" % self.modelActiveEffects[selection[1]][2]) self.effectImage.set_from_pixbuf(self.modelActiveEffects[selection[1]][0]) # resize the settingstable to accomodate the number of settings sliders required self.settingstable.resize(len(proplist), 2) count = 0 # iterate through the properties list, determine its value type # and show it where needed for property in proplist: # non readable params if not(property.flags & gobject.PARAM_READABLE): # create and attach a property name label to the settings table label = gtk.Label(property.name) label.set_alignment(1, 0.5) self.settingstable.attach(label, 0, 1, count, count+1, xoptions=gtk.FILL) rlabel = gtk.Label(_("-parameter not readable-")) self.settingstable.attach(rlabel, 1, 2, count, count+1) # just display non-writable param values elif not(property.flags & gobject.PARAM_WRITABLE): # create and attach a property name label to the settings table label = gtk.Label(property.name) label.set_alignment(1, 0.5) self.settingstable.attach(label, 0, 1, count, count+1, xoptions=gtk.FILL) wvalue = self.effectelement.get_property(property.name) if wvalue != None: wlabel = gtk.Label(wvalue) self.settingstable.attach(wlabel, 1, 2, count, count+1, xoptions=gtk.FILL) elif hasattr(property, "minimum") and hasattr(property, "maximum"): # create and attach a property name label to the settings table label = gtk.Label(property.name) label.set_alignment(1, 0.5) self.settingstable.attach(label, 0, 1, count, count+1, xoptions=gtk.FILL) # assume that it's numeric so we can use an HScale value = self.effectelement.get_property(property.name) # use these values for the slider range minValue = property.minimum maxValue = property.maximum # fix the ridiculously big min/max range values if needed if property.minimum < -self.propertyThreshold: minValue = -self.fixValue if property.maximum > self.propertyThreshold: maxValue = self.fixValue adj = gtk.Adjustment(value, minValue, maxValue) adj.connect("value_changed", self.SetSingleEffectSetting, property.name, property) self.sliderdict[property.name] = hscale = gtk.HScale(adj) hscale.set_value_pos(gtk.POS_RIGHT) # add step increment for mouse-wheel scrolling - proportional # to range in view. Must be at least the smallest interval # possible (determined by get_digits()) or scroll doesn't happen adj.step_increment = (maxValue - minValue) / 100.0 adj.step_increment = max(adj.step_increment, 1.0 / (10 ** hscale.get_digits())) # check for ints and change digits if not((property.value_type == gobject.TYPE_FLOAT) or (property.value_type == gobject.TYPE_DOUBLE)): hscale.set_digits(0) # add the slider to the settings table (with tooltips) self.sliderdict[property.name].set_tooltip_text(property.blurb) self.settingstable.attach(self.sliderdict[property.name], 1, 2, count, count+1, gtk.FILL|gtk.EXPAND) count += 1 # set up presets self.elementfactory = self.effectelement.get_factory().get_name() self.model = gtk.ListStore(str) self.presetscombo.set_model(self.model) self._LoadEffectPresets() # show the settings window self.settingstable.show() self.settingswindow.show_all() #_____________________________________________________________________ def _LoadEffectPresets(self): """ Loads the presets for the selected effect if there are any available. It then adds them to the effects presets combo box. The following checks are performed during this operation: a) the preset is on the system (in LADSPA_FACTORY_REGISTRY). b) the preset is for this plugin. """ self.availEffectPresets = [] self.model.clear() self.presets.FillEffectsPresetsRegistry() try: effectPresets = self.presets.effectpresetregistry["effects"][self.elementfactory] if self.elementfactory in Globals.LADSPA_FACTORY_REGISTRY: for pres in effectPresets: deps = effectPresets[pres]["dependencies"] if len(deps) == 1 and self.elementfactory in deps: self.availEffectPresets.append(pres) self.model.append([pres]) #self.presetscombo.append_text(pres) except KeyError: # no presets for this effect pass #_____________________________________________________________________ def OnSingleEffectSettingsClose(self, button): """ Close the effect settings window. Parameters: button -- reserved for GTK callbacks, don't use it explicitly. """ self.settingswindow.destroy() #_____________________________________________________________________ def SetSingleEffectSetting(self, slider, name, property): """ Set the value of a gstreamer property from an effects slider. Parameters: slider -- slider control which indicates the value to assign to property. name -- name of the property being set. property -- property being set. """ # grab the slider setting if not((property.value_type == gobject.TYPE_FLOAT) or (property.value_type == gobject.TYPE_DOUBLE)): value = int(slider.get_value()) else: value = slider.get_value() # set the gstreamer property self.instrument.effects[self.effectpos].set_property(name, value) #_____________________________________________________________________ def OnSingleEffectPresetSave(self, widget): """ Grab the effect properties and send it to the presets code to be saved. Parameters: widget -- reserved for GTK callbacks, don't use it explicitly. """ # grab the label from the combo label = self.presetscombo.get_active_text() if label == "": return effectdict = {} effect = self.instrument.effects[self.effectpos] effectelement = effect.get_factory().get_name() proplist = gobject.list_properties(effect) # for each property in proplist, add it to the effectdict dictionary for property in proplist: effectdict[property.name] = effect.get_property(property.name) # save the preset self.presets.SaveSingleEffect(label, effectdict, effectelement, "LADSPA") #_____________________________________________________________________ def OnSingleEffectPresetDelete(self, button): """ Removes the selected effect preset. Parameters: button -- reserved for GTK callbacks, don't use it explicitly. """ presetName = self.presetscombo.get_active_text() if presetName == "": return effect = self.instrument.effects[self.effectpos] effectName = effect.get_factory().get_name() self.presets.DeleteSingleEffect(presetName, effectName) #_____________________________________________________________________ def OnEffectChainPresetSave(self, button): """ Grabs the effects chain and sends it to the presets code to be saved. Parameters: button -- reserved for GTK callbacks, don't use it explicitly. """ # grab the label from the combo label = self.comboPresets.get_active_text() if label == "": return self.effectlist = [] for effect in self.instrument.effects: effectdict = {} effectsettings = {} proplist = gobject.list_properties(effect) for property in proplist: effectsettings[property.name] = effect.get_property(property.name) effectdict["effectelement"] = effect.get_factory().get_name() effectdict["effecttype"] = "LADSPA" effectdict["settings"] = effectsettings self.effectlist.append(effectdict) self.presets.SaveEffectChain(label, self.effectlist, self.instrument.instrType) #_____________________________________________________________________ def OnEffectChainPresetDelete(self, button): """ Removes the selected effects chain preset. Parameters: button -- reserved for GTK callbacks, don't use it explicitly. """ presetName = self.comboPresets.get_active_text() if presetName == "": return self.presets.DeleteEffectChain(presetName, self.instrument.instrType) #_____________________________________________________________________ def OnSingleEffectPresetChanged(self, combo): """ Loads a preset when it's selected from the single effect preset combo. Parameters: combo -- reserved for GTK callbacks, don't use it explicitly. """ presetname = name = combo.get_active_text() if presetname not in self.availEffectPresets: return settings = self.presets.LoadSingleEffect(presetname, self.effectelement.get_factory().get_name()) if not settings: return for item in settings: #self.sliderdict[str(item)] try: self.effectelement.set_property(item, settings[item]) self.sliderdict[str(item)].set_value(settings[item]) except: pass self.settingstable.show() self.settingswindow.show_all() #_____________________________________________________________________ def OnEffectChainPresetChanged(self, combo): """ Loads a preset when it's selected from the chain preset combo. Parameters: combo -- reserved for GTK callbacks, don't use it explicitly. """ # If we're still setting up the gui then we dont want to *do* anything when # we set the combobox so check the flag and quit if necessary if self.updatinggui == True: return presetname = name = combo.get_active_text() if presetname not in self.availpresets: return self.instrument.currentchainpreset = combo.get_active() settings = self.presets.LoadEffectChain(presetname, self.instrument.instrType) self.instrument.effects = [] for item in settings: effect = settings[item] prefs = effect['preferences'] setts = effect['settings'] effect = self.instrument.AddEffect(prefs['effectelement']) for sett in setts: try: setts[sett] effect.set_property(sett, setts[sett]) except: Globals.debug("problem loading preset property %s= %s"%(sett,setts[sett])) pass self.Update() #_____________________________________________________________________ def BringWindowToFront(self): """ Puts the InstrumentsEffectDialog on top of other windows. """ self.window.present() #_____________________________________________________________________ def OnResize(self, widget, event): """ This method is called when the Instrument Effect dialog is resized Parameters: widget -- GTK callback parameter. event -- GTK callback parameter. Returns: False -- continue GTK signal propagation. """ (self.width, self.height) = widget.get_size() return False #_____________________________________________________________________ def OnDestroy(self, widget=None, event=None): """ Called when the instrument effects dialog is destroyed Parameters: widget -- reserved for GTK callbacks, don't use it explicitly. event -- reserved for GTK callbacks, don't use it explicitly. """ Globals.settings.general["instrumenteffectwindowwidth"] = self.width Globals.settings.general["instrumenteffectwindowheight"] = self.height Globals.settings.write() #_____________________________________________________________________ #========================================================================= class CairoDialogHeaderImage(gtk.DrawingArea): """ Renders a banner for the top of the effects dialog using Cairo. """ _LEFT_GRADIENT_STOP_RGBA = (0, 0.99, 0.87, 0.38, 1) _RIGHT_GRADIENT_STOP_RGBA = (1, 0.93, 0.55, 0.16, 1) _TEXT_RGBA = (0.93, 0.55, 0.16, 1) _FONT_SIZE = 40 _LEFT_LOGO_INDENT = 7 _LEFT_TEXT_INDENT = 56 #_____________________________________________________________________ def __init__(self, headerText): """ Creates a new instance of CairoDialogHeaderImage. Parameters: headerText -- text to be displayed at the header. """ gtk.DrawingArea.__init__(self) self.headerText = headerText self.connect("configure_event", self.OnSizeChanged) self.connect("expose-event", self.OnDraw) self.logo = cairo.ImageSurface.create_from_png(os.path.join(Globals.IMAGE_PATH, "jokosher.png")) self.source = cairo.ImageSurface(cairo.FORMAT_ARGB32, self.allocation.width, self.allocation.height) self.GenerateCachedImage() #_____________________________________________________________________ def OnSizeChanged(self, obj, evt): """ Called when the widget's size changes. Parameters: obj -- reserved for Cairo callbacks, don't use it explicitly. *CHECK* evt -- reserved for Cairo callbacks, don't use it explicitly. *CHECK* """ if self.allocation.width != self.source.get_width() or self.allocation.height != self.source.get_height(): self.source = cairo.ImageSurface(cairo.FORMAT_ARGB32, self.allocation.width, self.allocation.height) self.GenerateCachedImage() #_____________________________________________________________________ def GenerateCachedImage(self): """ Renders the image so that we don't have to re-render it every time there is an expose event from a mouse move or something. """ rect = self.get_allocation() ctx = cairo.Context(self.source) ctx.set_antialias(cairo.ANTIALIAS_SUBPIXEL) # Create our gradient pat = cairo.LinearGradient(0.0, 0.0, rect.width, 0) pat.add_color_stop_rgba(*self._RIGHT_GRADIENT_STOP_RGBA) pat.add_color_stop_rgba(*self._LEFT_GRADIENT_STOP_RGBA) # Fill the widget ctx.rectangle(0, 0, rect.width, rect.height) ctx.set_source(pat) ctx.fill() #Paint the Jokosher logo on top (x=7, y=8 centres the logo) logoPos = (rect.height - self.logo.get_height()) / 2 ctx.set_source_surface(self.logo, self._LEFT_LOGO_INDENT, int(logoPos)) ctx.paint() ctx.set_source_rgba(*self._TEXT_RGBA) textPos = rect.height - ((rect.height - self._FONT_SIZE) / 2) - 5 ctx.move_to(self._LEFT_TEXT_INDENT, int(textPos)) ctx.set_font_size(self._FONT_SIZE) ctx.show_text(self.headerText) ctx.stroke() #_____________________________________________________________________ def OnDraw(self, widget, event): """ Handles the GTK expose event. Parameters: widget -- reserved for GTK callbacks, don't use it explicitly. event -- reserved for GTK callbacks, don't use it explicitly. Returns: False -- indicates the GTK signal to: 1) continue propagating the regular signal. 2) stop calling the callback on a timeout_add. """ ctx = widget.window.cairo_create() #Don't repaint the whole area unless necessary ctx.rectangle(event.area.x, event.area.y, event.area.width, event.area.height) ctx.clip() # Paint the cached image across the widget ctx.set_source_surface(self.source, 0, 0) ctx.paint() return False #_____________________________________________________________________ #=========================================================================