def draw_analog_assignments(self, controllers): zone = self.ZONE_ASSIGNMENTS self.erase_zone(zone) # spacing and scaling of text width_per_control = self.width text_per_control = self.width num = len(controllers) if num > 0: width_per_control = int(round(self.width / num)) text_per_control = width_per_control - 16 # minus width of control icon x = 0 for k, v in controllers.items(): control_type = util.DICT_GET(v, Token.TYPE) color = util.DICT_GET(v, Token.COLOR) if color is None: # color not specified for control in config file category = util.DICT_GET(v, Token.CATEGORY) color = self.get_category_color(category) name = k.split(":")[1] n = self.shorten_name(name, text_per_control) if control_type == Token.KNOB: self.draw_knob(n, x, color) if control_type == Token.EXPRESSION: self.draw_pedal(n, x, color) x += width_per_control self.refresh_zone(zone)
def update_wifi(self, wifi_status): if not self.supports_toolbar: return if util.DICT_GET(wifi_status, 'hotspot_active'): img = "wifi_orange.png" elif util.DICT_GET(wifi_status, 'wifi_connected'): img = "wifi_silver.png" else: img = "wifi_gray.png" path = os.path.join(self.imagedir, img) self.change_tool_img(self.tool_wifi, path)
def get_category_color(self, category): color = "Silver" if category: c = util.DICT_GET(self.category_color_map, category) if c: color = c if isinstance(c, tuple) else self.valid_color(c) return color
def get_pedalboard_bundle_from_mod(self): # Assumes the caller has already checked for existence of the file mod_bundle = None with open(self.pedalboard_modification_file, 'r') as file: j = json.load(file) mod_bundle = util.DICT_GET(j, 'pedalboard') return mod_bundle
def draw_analog_assignments(self, controllers): zone = 1 self.erase_zone(zone) exp = Token.NONE knob = Token.NONE for k, v in controllers.items(): control_type = util.DICT_GET(v, Token.TYPE) s = k.split(":") text = "%s:%s" % (self.shorten_name(s[0], self.plugin_width), self.shorten_name(s[1], self.plugin_width_medium)) if control_type == Token.EXPRESSION: exp = text elif control_type == Token.KNOB: knob = text # Expression Pedal assignment self.draw[zone].line(((0, 5), (8, 1)), True, 1) self.draw[zone].line(((0, 5), (8, 5)), True, 2) self.draw[zone].text((10, 2), exp, True, self.small_font) # Tweak knob assignment x = 66 self.draw[zone].ellipse(((x, 0), (x + 6, 6)), True, 1) self.draw[zone].line(((x + 3, 0), (x + 3, 2)), False, 1) self.draw[zone].text((x + 9, 2), knob, True, self.small_font) self.refresh_zone(zone)
def poll_modui_changes(self): # This poll looks for changes made via the MOD UI and tries to sync the pi-Stomp hardware # Look for a change of pedalboard # # If the pedalboard_modification_file timestamp has changed, extract the bundle path and set current pedalboard # # TODO this is an interim solution until better MOD-UI to pi-stomp event communication is added # if Path(self.pedalboard_modification_file).exists(): ts = os.path.getmtime(self.pedalboard_modification_file) if ts == self.pedalboard_change_timestamp: return # Timestamp changed self.pedalboard_change_timestamp = ts self.lcd.draw_info_message("Loading...") with open(self.pedalboard_modification_file, 'r') as file: j = json.load(file) mod_bundle = util.DICT_GET(j, 'pedalboard') if mod_bundle: logging.info("Pedalboard changed via MOD from: %s to: %s" % (self.current.pedalboard.bundle, mod_bundle)) pb = self.pedalboards[mod_bundle] self.set_current_pedalboard(pb)
def __init__(self, plugin_info, value, binding): self.name = util.DICT_GET(plugin_info, Token.SHORTNAME) # possibly use name if shortName is None if self.name is None: self.name = util.DICT_GET(plugin_info, Token.NAME) self.symbol = util.DICT_GET(plugin_info, Token.SYMBOL) self.minimum = util.DICT_GET(util.DICT_GET(plugin_info, Token.RANGES), Token.MINIMUM) self.maximum = util.DICT_GET(util.DICT_GET(plugin_info, Token.RANGES), Token.MAXIMUM) self.value = value self.binding = binding
def update_lcd_title(self): invert_pb = False invert_pre = False if self.top_encoder_mode == TopEncoderMode.PEDALBOARD_SELECT: invert_pb = True if self.top_encoder_mode == TopEncoderMode.PRESET_SELECT: invert_pre = True self.lcd.draw_title( self.current.pedalboard.title, util.DICT_GET(self.current.presets, self.current.preset_index), invert_pb, invert_pre)
def create_analog_controls(self, cfg): if cfg is None or (Token.HARDWARE not in cfg) or (Token.ANALOG_CONTROLLERS not in cfg[Token.HARDWARE]): return midi_channel = self.__get_real_midi_channel(cfg) cfg_c = cfg[Token.HARDWARE][Token.ANALOG_CONTROLLERS] if cfg_c is None: return for c in cfg_c: if Util.DICT_GET(c, Token.DISABLE) is True: continue adc_input = Util.DICT_GET(c, Token.ADC_INPUT) midi_cc = Util.DICT_GET(c, Token.MIDI_CC) threshold = Util.DICT_GET(c, Token.THRESHOLD) control_type = Util.DICT_GET(c, Token.TYPE) if adc_input is None: logging.error("Analog control specified without %s" % Token.ADC_INPUT) continue if midi_cc is None: logging.error("Analog control specified without %s" % Token.MIDI_CC) continue if threshold is None: threshold = 16 # Default, 1024 is full scale control = AnalogMidiControl.AnalogMidiControl( self.spi, adc_input, threshold, midi_cc, midi_channel, self.midiout, control_type, c) self.analog_controls.append(control) key = format("%d:%d" % (midi_channel, midi_cc)) self.controllers[key] = control
def create_footswitches(self, cfg): if cfg is None or (Token.HARDWARE not in cfg) or (Token.FOOTSWITCHES not in cfg[Token.HARDWARE]): return cfg_fs = cfg[Token.HARDWARE][Token.FOOTSWITCHES] if cfg_fs is None: return midi_channel = self.__get_real_midi_channel(cfg) idx = 0 for f in cfg_fs: if Util.DICT_GET(f, Token.DISABLE) is True: continue di = Util.DICT_GET(f, Token.DEBOUNCE_INPUT) if self.debounce_map and di in self.debounce_map: gpio_input = self.debounce_map[di] else: gpio_input = Util.DICT_GET(f, Token.GPIO_INPUT) gpio_output = Util.DICT_GET(f, Token.GPIO_OUTPUT) midi_cc = Util.DICT_GET(f, Token.MIDI_CC) id = Util.DICT_GET(f, Token.ID) if gpio_input is None: logging.error("Switch specified without %s or %s" % (Token.DEBOUNCE_INPUT, Token.GPIO_INPUT)) continue fs = Footswitch.Footswitch(id if id else idx, gpio_input, gpio_output, midi_cc, midi_channel, self.midiout, refresh_callback=self.refresh_callback) self.footswitches.append(fs) idx += 1
def load_bundle(self, bundlepath, plugin_dict): # Load the bundle, return the single plugin for the pedalboard plugin = self.get_pedalboard_plugin(self.world, bundlepath) # check if the plugin is a pedalboard def fill_in_type(node): if node is not None and node.is_uri(): return node return None u = self.world.new_uri( "http://www.w3.org/1999/02/22-rdf-syntax-ns#type") plugin_types = [ i for i in util.LILV_FOREACH(plugin.get_value(u), fill_in_type) ] if "http://moddevices.com/ns/modpedal#Pedalboard" not in plugin_types: raise Exception( 'get_pedalboard_info(%s) - plugin has no mod:Pedalboard type'. format(bundlepath)) # Walk ports starting from capture1 to determine general plugin order # TODO can this be generalized to use the chase_tail function? plugin_order = [] ports = plugin.get_value(self.uri_port) for port in ports: if port is None: continue tail = self.world.get(None, self.uri_tail, port) # TODO could end up being capture2 if tail is None: continue head = self.world.get(tail, self.uri_head, None) if head is not None: block = self.world.get(None, self.uri_port, head) if block is not None: self.chase_tail(block, plugin_order) break # Iterate blocks (plugins) plugins_unordered = {} plugins_extra = [] blocks = plugin.get_value(self.uri_block) for block in blocks: if block is None or block.is_blank(): continue # Add plugin data (from plugin registry) to global plugin dictionary plugin_info = {} prototype = self.world.find_nodes(block, self.world.ns.lv2.prototype, None) if len(prototype) > 0: logging.debug("prototype %s" % prototype[0]) plugin_uri = str(prototype[0]) # plugin.get_uri() if plugin_uri not in plugin_dict: plugin_info = self.get_plugin_data(plugin_uri) if plugin_info: logging.debug("added %s" % plugin_uri) plugin_dict[plugin_uri] = plugin_info else: plugin_info = plugin_dict[plugin_uri] #category = util.DICT_GET(plugin_info, Token.CATEGORY) # Extract Parameter data instance_id = str(block.get_path()).replace(bundlepath, "", 1) nodes = self.world.find_nodes(block, self.world.ns.lv2.port, None) parameters = {} if len(nodes) > 0: # These are the port nodes used to define parameter controls for port in nodes: param_value = self.world.get(port, self.uri_value, None) logging.debug("port: %s value: %s" % (port, param_value)) binding = self.world.get(port, self.world.ns.midi.binding, None) if binding is not None: controller_num = self.world.get( binding, self.world.ns.midi.controllerNumber, None) channel = self.world.get(binding, self.world.ns.midi.channel, None) if (controller_num is not None) and (channel is not None): binding = "%d:%d" % (self.world.new_int( channel), self.world.new_int(controller_num)) logging.debug(" binding %s" % binding) path = str(port) symbol = os.path.basename(path) value = None if param_value is not None: if param_value.is_float(): value = float(self.world.new_float(param_value)) elif param_value.is_int(): value = int(self.world.new_int(param_value)) else: value = str(value) # Bypass "parameter" is a special case without an entry in the plugin definition if symbol == Token.COLON_BYPASS: info = { "shortName": "bypass", "symbol": symbol, "ranges": { "minimum": 0, "maximum": 1 } } # TODO tokenize v = False if value is 0 else True param = Parameter.Parameter(info, v, binding) parameters[symbol] = param continue # don't try to find matching symbol in plugin_dict # Try to find a matching symbol in plugin_dict to obtain the remaining param details try: plugin_params = plugin_info[Token.PORTS][ Token.CONTROL][Token.INPUT] except KeyError: logging.warning( "plugin port info not found, could be missing LV2 for: %s", instance_id) continue for pp in plugin_params: sym = util.DICT_GET(pp, Token.SYMBOL) if sym == symbol: #logging.debug("PARAM: %s %s %s" % (util.DICT_GET(pp, 'name'), info[uri], category)) param = Parameter.Parameter(pp, value, binding) logging.debug("Param: %s %s %4.2f %4.2f %s" % (param.name, param.symbol, param.minimum, value, binding)) parameters[symbol] = param #logging.debug(" Label: %s" % label) inst = Plugin.Plugin(instance_id, parameters, plugin_info) try: index = plugin_order.index(block) plugins_unordered[index] = inst except: plugins_extra.append(inst) #logging.debug("dump: %s" % inst.to_json()) # Add "extra" plugins (those not part of the tail_chase order) to the plugins_unordered dict max_index = len(plugins_unordered) for e in plugins_extra: plugins_unordered[max_index] = e max_index = max_index + 1 # Sort the dictionary based on their order index and add to the pedalboard.plugin list # TODO improve the creation (tail chasing, sorting, dict>list conversion) if max_index > 0: sorted_dict = dict( sorted(plugins_unordered.items(), key=operator.itemgetter(0))) for i in range(0, len(sorted_dict)): val = sorted_dict.get(i) if val is not None: self.plugins.append(val) # Done obtaining relevant lilv for the pedalboard return