from data_centre import plugin_collection from data_centre.plugin_collection import MidiFeedbackPlugin import mido class MidiFeedbackAPCKey25Plugin(MidiFeedbackPlugin): disabled = False status = {} def __init__(self, plugin_collection): super().__init__(plugin_collection) self.description = 'Outputs feedback to APC Key 25' def get_note(self,action,default): bind = self.pc.midi_input.find_binding_for_action(action) if bind is not None and 'note_on' in bind: return int(bind.split(' ')[1]) else: print ("bind is %s, returning default" % bind) return default def set_midi_device(self, device): super().set_midi_device(device) self.last_state = None self.init_notes() def init_notes(self): self.NOTE_PLAY_SHADER = self.get_note('play_shader_0_0',32) self.NOTE_SHADER_FEEDBACK = self.get_note('toggle_feedback',85) self.NOTE_SCENE_LAUNCH_COLUMN = self.get_note('toggle_shader_layer_0',82) self.NOTE_MODULATION_COLUMN = self.get_note('select_shader_modulation_slot_0', self.NOTE_SCENE_LAUNCH_COLUMN) self.NOTE_CAPTURE_PREVIEW = self.get_note('toggle_capture_preview', 86) self.NOTE_CLIP_STATUS_ROW = self.get_note('toggle_automation_clip_0', 8) self.NOTE_SHADER_PRESET_ROW = self.get_note('select_preset_0', 0) self.NOTE_SHADER_LAYER_ON = [ #82, 83, 84 self.get_note('toggle_shader_layer_%i'%i, 82+i) for i in range(0,3) ] def supports_midi_feedback(self, device_name): supported_devices = ['APC Key 25'] for supported_device in supported_devices: if device_name.startswith(supported_device): return True def set_status(self, command='note_on', note=None, velocity=None): self.status[note] = { 'command': command, 'note': note, 'velocity': velocity } #print("set status to %s: %s" % (note, self.status[note])) def send_command(self, command='note_on', note=None, velocity=None): #print("send_command(%s, %s)" % (note, velocity)) self.midi_feedback_device.send( mido.Message(command, note=note, velocity=velocity) ) def feedback_shader_feedback(self, on): self.set_status(note=self.NOTE_SHADER_FEEDBACK, velocity=int(on)) def feedback_capture_preview(self, on): self.set_status(note=self.NOTE_CAPTURE_PREVIEW, velocity=int(on)) def feedback_shader_on(self, layer, slot, colour=None): if colour is None: colour = self.COLOUR_GREEN self.set_status(note=(self.NOTE_PLAY_SHADER-(layer)*8)+slot, velocity=int(colour)) def feedback_shader_off(self, layer, slot): self.set_status(note=(self.NOTE_PLAY_SHADER-(layer)*8)+slot, velocity=self.COLOUR_OFF) def feedback_shader_layer_on(self, layer): self.set_status(note=self.NOTE_SHADER_LAYER_ON[layer], velocity=self.COLOUR_GREEN) def feedback_shader_layer_off(self, layer): self.set_status(note=self.NOTE_SHADER_LAYER_ON[layer], velocity=self.COLOUR_OFF) def feedback_show_layer(self, layer): self.set_status(note=70, velocity=layer) def feedback_show_modulation(self, slot): for i in range(self.NOTE_MODULATION_COLUMN,self.NOTE_MODULATION_COLUMN+4): if slot==i-self.NOTE_MODULATION_COLUMN: self.set_status(note=i, velocity=self.COLOUR_GREEN) else: self.set_status(note=i, velocity=self.COLOUR_OFF) def feedback_plugin_status(self): from data_centre.plugin_collection import SequencePlugin try: from plugins.ShaderLoopRecordPlugin import ShaderLoopRecordPlugin for plugin in self.pc.get_plugins(SequencePlugin): if isinstance(plugin, ShaderLoopRecordPlugin): NOTE_PLAY_STATUS = 65 NOTE_RECORD_STATUS = 66 NOTE_OVERDUB_STATUS = 67 #NOTE_CLIP_STATUS_ROW = 8 colour = self.COLOUR_OFF if plugin.is_playing(): colour = self.COLOUR_GREEN if plugin.is_paused(): colour += self.BLINK self.set_status(command='note_on', note=NOTE_PLAY_STATUS, velocity=colour) colour = self.COLOUR_OFF if plugin.recording: colour = self.COLOUR_GREEN if plugin.is_ignoring(): colour += self.BLINK self.set_status(command='note_on', note=NOTE_RECORD_STATUS, velocity=colour) colour = self.COLOUR_OFF if plugin.overdub: colour = self.COLOUR_RED if plugin.is_paused() or plugin.is_ignoring(): colour += self.BLINK self.set_status(command='note_on', note=NOTE_OVERDUB_STATUS, velocity=colour) for i in range(plugin.MAX_CLIPS): if i in plugin.running_clips: if plugin.is_playing() and not plugin.is_paused(): colour = self.COLOUR_GREEN else: colour = self.COLOUR_AMBER if plugin.selected_clip==i: #blink if selected colour += self.BLINK elif plugin.selected_clip==i: colour = self.COLOUR_RED_BLINK else: colour = self.COLOUR_OFF self.set_status(command='note_on', note=self.NOTE_CLIP_STATUS_ROW+i, velocity=colour) """ # doesnt really work well, but shows progress of clip in leds if plugin.is_playing() and not plugin.is_paused(): for i in range(0,plugin.MAX_CLIPS): if i==int(plugin.position*plugin.MAX_CLIPS): self.set_status(command='note_on', note=self.NOTE_CLIP_STATUS_ROW+i, velocity=self.COLOUR_GREEN) """ except Exception as e: pass #print ("Warning: Failed when running plugin feedback for ShaderLoopRecordPlugin:\t%s" % str(e)) try: from plugins.ShaderQuickPresetPlugin import ShaderQuickPresetPlugin #print ("feedback_plugin_status") for plugin in self.pc.get_plugins(ShaderQuickPresetPlugin): #print ("for plugin %s" % plugin) for pad in range(0,8): #print ("checking selected_preset %s vs pad %s" % (plugin.selected_preset, pad)) colour = self.COLOUR_OFF if plugin.presets[pad] is not None: colour = self.COLOUR_AMBER if plugin.last_recalled==pad: colour = self.COLOUR_GREEN if plugin.selected_preset==pad: if plugin.presets[pad] is None: colour = self.COLOUR_RED colour += self.BLINK self.set_status(command='note_on', note=self.NOTE_SHADER_PRESET_ROW+pad, velocity=colour) except Exception as e: pass #print ("Warning: Failed when running plugin feedback for ShaderQuickPresetPlugin:\t%s" % str(e)) BLINK = 1 COLOUR_OFF = 0 COLOUR_GREEN = 1 COLOUR_GREEN_BLINK = 2 COLOUR_RED = 3 COLOUR_RED_BLINK = 4 COLOUR_AMBER = 5 COLOUR_AMBER_BLINK = 6 def refresh_midi_feedback(self): # show which layer is selected (where parameter offset goes to) self.feedback_show_layer(self.pc.data.shader_layer) # show if internal feedback (the shader layer kind) is enabled if self.pc.data.feedback_active and not self.pc.data.function_on: self.feedback_shader_feedback(self.COLOUR_GREEN) #elif self.pc.data.settings['shader']['X3_AS_SPEED']['value'] == 'enabled' and self.pc.data.function_on: # self.feedback_shader_feedback(self.COLOUR_GREEN_BLINK) else: self.feedback_shader_feedback(self.COLOUR_OFF) if self.pc.message_handler.actions.display.capture.is_previewing and not self.pc.data.function_on: self.feedback_capture_preview(self.COLOUR_GREEN) else: self.feedback_capture_preview(self.COLOUR_OFF) if self.pc.data.function_on: self.feedback_show_modulation(self.pc.shaders.selected_modulation_slot) self.feedback_plugin_status() for n,shader in enumerate(self.pc.message_handler.shaders.selected_shader_list): #print ("%s: in refresh_midi_feedback, got shader: %s" % (n,shader)) # show if layer is running or not if not self.pc.data.function_on: if self.pc.message_handler.shaders.selected_status_list[n] == '▶': self.feedback_shader_layer_on(n) else: self.feedback_shader_layer_off(n) for x in range(0,8): if 'slot' in shader and shader.get('slot',None)==x: if self.pc.message_handler.shaders.selected_status_list[n] == '▶': # show that slot is selected and running self.feedback_shader_on(n, x, self.COLOUR_GREEN) else: # show that slot is selected but not running self.feedback_shader_on(n, x, self.COLOUR_AMBER_BLINK) elif self.pc.data.shader_bank_data[n][x]['path']: # show that slot is full but not selected self.feedback_shader_on(n, x, self.COLOUR_AMBER) else: # has nothing in slot self.feedback_shader_off(n, x) self.update_device() #print("refresh_midi_feedback") last_state = None def update_device(self): from copy import deepcopy #print("in update device status is %s" % self.status) for i,c in self.status.items(): #'print("comparing\n%s to\n%s" % (c, self.last_state[i])) if self.last_state is None or (i not in self.last_state or self.last_state[i]!=c): #print("got command: %s: %s" % (i,c)) self.send_command(**c) self.last_state = deepcopy(self.status)