diff --git a/json_objects/midi_action_mapping_APC Key 25.json b/json_objects/midi_action_mapping_APC Key 25.json index 2ccd933..5dbb7dd 100644 --- a/json_objects/midi_action_mapping_APC Key 25.json +++ b/json_objects/midi_action_mapping_APC Key 25.json @@ -42,27 +42,27 @@ "control_change 48": { "DEFAULT": ["set_the_shader_param_0_layer_offset_0_continuous","set_strobe_amount_continuous"], "NAV_DETOUR": ["set_detour_speed_position_continuous"], - "NAV_WJMX": ["wj_set_position_N_x"], + "NAV_WJMX": ["wj_set_position_N:x"], "NAV_MANI": ["set_variable_A"], "NAV_LFO": ["set_lfo_modulation_0_level"] }, "control_change 49": { "DEFAULT": ["set_the_shader_param_1_layer_offset_0_continuous","set_shader_speed_layer_0_amount"], "NAV_DETOUR": ["set_detour_start_continuous"], - "NAV_WJMX": ["wj_set_position_N_y"], + "NAV_WJMX": ["wj_set_position_N:y"], "NAV_MANI": ["f:sin(x*pi):|set_variable_SIN"], "NAV_LFO": ["set_lfo_modulation_1_level"] }, "control_change 50": { "DEFAULT": ["set_the_shader_param_2_layer_offset_0_continuous","set_shader_speed_layer_1_amount"], "NAV_DETOUR": ["set_detour_end_continuous"], - "NAV_WJMX": ["wj_set_colour_T_x"], + "NAV_WJMX": ["wj_set_colour_T:x"], "NAV_LFO": ["set_lfo_modulation_2_level"] }, "control_change 51": { "DEFAULT": ["set_the_shader_param_3_layer_offset_0_continuous","set_shader_speed_layer_2_amount"], "NAV_DETOUR": ["set_detour_end_continuous"], - "NAV_WJMX": ["wj_set_colour_T_y"], + "NAV_WJMX": ["wj_set_colour_T:y"], "NAV_LFO": ["set_lfo_modulation_3_level"] }, "control_change 52": { @@ -74,17 +74,17 @@ "control_change 53": { "DEFAULT": ["set_the_shader_param_1_layer_offset_1_continuous","set_param_1_layer_offset_0_modulation_level_continuous"], "NAV_DETOUR": ["set_detour_start_continuous"], - "NAV_WJMX": ["wj_set_back_colour_x","wj_set_back_wash_colour_x"] + "NAV_WJMX": ["wj_set_back_colour:h","wj_set_dsk_level"] }, "control_change 54": { "DEFAULT": ["set_the_shader_param_2_layer_offset_1_continuous","set_param_2_layer_offset_0_modulation_level_continuous"], "NAV_DETOUR": ["set_detour_end_continuous"], - "NAV_WJMX": ["wj_set_back_colour_y","wj_set_back_wash_colour_y"] + "NAV_WJMX": ["wj_set_back_colour:s","wj_set_colour_gain_T:y"] }, "control_change 55": { "DEFAULT": ["set_the_shader_param_3_layer_offset_1_continuous","set_param_3_layer_offset_0_modulation_level_continuous"], "NAV_DETOUR": ["set_detour_end_continuous"], - "NAV_WJMX": ["wj_set_back_colour_z","wj_set_back_wash_colour_z"] + "NAV_WJMX": ["wj_set_back_colour:v","wj_set_back_wash_colour:z"] }, "control_change 56": { "DEFAULT": ["set_the_shader_param_0_layer_offset_2_continuous"], diff --git a/plugins/WJSendPlugin.py b/plugins/WJSendPlugin.py index d164a24..740a9b3 100644 --- a/plugins/WJSendPlugin.py +++ b/plugins/WJSendPlugin.py @@ -6,7 +6,7 @@ import threading class WJSendPlugin(ActionsPlugin, SequencePlugin, DisplayPlugin, ModulationReceiverPlugin, AutomationSourcePlugin): disabled = False#True - DEBUG = False#True + DEBUG = False #True ser = None # from http://depot.univ-nc.nc/sources/boxtream-0.9999/boxtream/switchers/panasonic.py """serial.Serial(device, baudrate=9600, @@ -17,18 +17,6 @@ class WJSendPlugin(ActionsPlugin, SequencePlugin, DisplayPlugin, ModulationRecei rtscts=True, # TODO : test without this one timeout=timeout)""" - """self.commands = { - 'VCG:': { - 'name': 'Colour Corrector Gain', - 'cmd': 'VCG:', - }, - 'VCC:': { - 'name': 'Colour Corrector XY', - 'cmd': 'VCC', - 'callback': self.set_colour - } - }""" - THROTTLE = 1 # milliseconds to wait between refreshing parameters def __init__(self, plugin_collection): @@ -38,6 +26,9 @@ class WJSendPlugin(ActionsPlugin, SequencePlugin, DisplayPlugin, ModulationRecei print ("WJSendPlugin is disabled, not opening serial") return + for cmd,struct in self.commands.items(): + self.command_by_queue[struct['queue']] = struct + self.pc.actions.tk.after(500, self.refresh) # methods/vars for AutomationSourcePlugin @@ -64,20 +55,32 @@ class WJSendPlugin(ActionsPlugin, SequencePlugin, DisplayPlugin, ModulationRecei # methods for ModulationReceiverPlugin - receives changes to the in-built modulation levels (-1 to +1) # experimental & hardcoded ! # TODO: make this not hardcoded and configurable mapping modulation to parameters, preferably on-the-fly.. + modulation_value = [0.0]*4 def set_modulation_value(self, param, value): + + self.modulation_value[param] = 1.0-value ## invert so that no signal always gives a value .. + # take modulation value and throw it to local parameter if self.DEBUG: print("||||| WJSendPlugin received set_modulation_value for param %s with value %s!" % (param, value)) - if param==0: - self.set_mix((0.5+value)/2) - elif param==1: - self.set_colour('T', 'x', (0.5+value)/2) - elif param==2: - self.set_colour('T', 'y', (0.5+value)/2) - elif param==3: - self.set_back_colour('x', (0.5+value)/2) - else: - if self.DEBUG: print("\tunknown param %s!" % param) - + #v = (0.5+value)/2 + """mapped = [ + 'mix', + 'colour_T', + #'colour_T', + 'back_colour:x' + ]""" + #self.catch_all(*mapped[param].split(":")+[v]) + #self.commands[ + # find which commands are mapped to this modulation, and trigger a send of them + # so that they update with the new modulation value + for queue,cmd in sorted(self.command_by_queue.items()): + if cmd.get('modulation') is not None: + if self.DEBUG: print("\tparam %s, checking modulation %s" % (param, cmd.get('modulation'))) + if len(cmd.get('modulation')[param])>0: + self.DEBUG: print("\tyes! sending update of values? %s" % [x for x in cmd['arguments'].values() ]) + self.send_buffered(cmd['queue'], cmd['form'], [x for x in cmd['arguments'].values() ]) + continue + #methods for DisplayPlugin def show_plugin(self, display, display_mode): from tkinter import Text, END @@ -135,6 +138,7 @@ class WJSendPlugin(ActionsPlugin, SequencePlugin, DisplayPlugin, ModulationRecei try: # sorting the commands that are sent seems to fix jerk and lag that is otherwise pretty horrendous for queue, command in sorted(self.queue.items()): + # TODO: modulate the parameters self.send_buffered(queue, command[0], command[1]) #self.queue.clear() except Exception: @@ -154,9 +158,11 @@ class WJSendPlugin(ActionsPlugin, SequencePlugin, DisplayPlugin, ModulationRecei last = {} def send_buffered(self, queue, form, args, record = True): # only send if new command is different to the last one we sent - if self.last.get(queue)!=(form,args): + mod_args = self.modulate_arguments(self.command_by_queue.get(queue), args) + if self.last.get(queue)!=(form,mod_args): #print("WJSendPlugin>> send_buffered attempting to parse queue\t%s with form\t'%s' and args\t%s" % (queue, form, args)) - output = form.format(*args) + # TODO: actually output modulated version of args + output = form.format(*mod_args) self.send_serial_string(output) self.last[queue] = (form,args) if record: @@ -176,74 +182,91 @@ class WJSendPlugin(ActionsPlugin, SequencePlugin, DisplayPlugin, ModulationRecei def parserlist(self): return [ ( r"^open_serial$", self.open_serial ), - ( r"^wj_send_serial_([0-9a-zA-Z:]*)$", self.send_serial_string ), - ( r"^wj_set_colour_([A|B|T])_([x|y])$", self.set_colour ), - ( r"^wj_set_back_colour_([x|y|z])$", self.set_back_colour ), - ( r"^wj_set_back_wash_colour_([x|y|z])$", self.set_back_wash_colour ), - ( r"^wj_set_position_([N|L])_([x|y])$", self.set_position ), - ( r"^wj_set_mix$", self.set_mix ), - ( r"^wj_send_append_pad_([0-9]*)_([[:0-9a-zA-Z]*)$", self.send_append_pad ), - ( r"^wj_send_append_([:0-9a-zA-Z]*)$", self.send_append ), + ( r"^wj_send_serial:([0-9a-zA-Z:]*)$", self.send_serial_string ), + #( r"^wj_set_colour:([A|B|T])_([x|y])$", self.set_colour ), + #( r"^wj_set_back_colour:([x|y|z])$", self.set_back_colour ), + #( r"^wj_set_position:([N|L])_([x|y])$", self.set_position ), + #( r"^wj_set_mix$", self.set_mix ), + ( r"^wj_send_append_pad:([0-9]*)_([[:0-9a-zA-Z]*)$", self.send_append_pad ), + ( r"^wj_send_append:([:0-9a-zA-Z]*)$", self.send_append ), + ( r"^wj_set_([a-zA-Z_]*)[:]?([a-zA-Z_]*)$", self.catch_all ) ] - # methods for handling some Panasonic control settings as parameters - # TODO: come up with a way to represent this programmatically - # TODO: add more! + def catch_all(self, param, argument_name, value): + #print ("got catch-all %s, %s, %s" % (param, argument_name, value)) + #arguments = packed_arguments.split("_") + [ value ] + #print("commands looks like %s" % self.commands) + msg = self.commands[param] + if len(msg['arg_names'])==1: argument_name = msg['arg_names'][0] + msg['arguments'][argument_name] = int(value*255) # = arguments + self.send(msg['queue'], msg['form'], [ msg['arguments'][p] for p in msg['arg_names'] ] ) - colour_x = 127 - colour_y = 127 - def set_colour(self, chan, dim, value): - # chan can be A, B or T (both) - if dim=='x': - self.colour_x = int(255*value) - elif dim=='y': - self.colour_y = int(255*value) + def modulate_arguments(self, command, args): + args = args.copy() + if self.DEBUG: print("modulate_arguments passed %s and %s" % (command,args)) + for slot in range(0,4): + modlevels = command.get('modulation',[{}]*4)[slot] + if self.DEBUG: print("\tfor modulate_arguments for slot %s got modlevels: %s" % (slot, modlevels)) + #if len(command.get('modulation',[{}]*4)[slot])>0: + for i,m in enumerate(modlevels.values()): + if m>0.0: + if self.DEBUG: print("\t\tupdating modulation slot %s with %s * %s" % (i, m, self.modulation_value[slot])) + newvalue = int(args[i] * m * self.modulation_value[slot]) + if self.DEBUG: print("\t\tnewvalue is %s" %newvalue) + args[i] = newvalue #int(args[i] * (int(255 * m * self.modulation_value[slot]))) - self.send('VCC', "VCC:{}{:02X}{:02X}", [chan, self.colour_x, self.colour_y]) + """ if command.get('modulation',{}).get(arg_name,None) is not None: + args[argindex] = args[argindex] * command.get('modulation',{}).get(arg_name,[0.0]*4)[argindex] + #for slot in range(0,4): # for each modulation slot""" + return args - # RGB control of matte colour! - back_colour_x = 127 - back_colour_y = 127 - back_colour_z = 127 - def set_back_colour(self, dim, value): - # chan can be A, B or T (both) - if dim=='x': - self.back_colour_x = int(255*value) - elif dim=='y': - self.back_colour_y = int(255*value) - elif dim=='z': - self.back_colour_z = int(255*value) - - self.send('VBM', "VBM:{:02X}{:02X}{:02X}", [ self.back_colour_x,self.back_colour_y,self.back_colour_z ]) - - # this doesnt seem to work on WJ-MX30 at least, or maybe i dont know how to get it into the right mode? - # TODO: replace with downstream key control - back_wash_colour_x = 127 - back_wash_colour_y = 127 - back_wash_colour_z = 127 - def set_back_wash_colour(self, dim, value): - # chan can be A, B or T (both) - if dim=='x': - self.back_wash_colour_x = int(255*value) - elif dim=='y': - self.back_wash_colour_y = int(255*value) - elif dim=='z': - self.back_wash_colour_z = int(255*value) - - self.send('VBW', "VBW:{:02X}{:02X}{:02X}", [ self.back_wash_colour_x,self.back_wash_colour_y,self.back_wash_colour_z ] ) - - # positioner joystick - position_x = 127 - position_y = 127 - def set_position(self, mode, dim, value): - if dim=='y': # yes, y is really x! - self.position_x = int(255*value) - elif dim=='x': # yes, x is really y! - self.position_y = int(255*value) - - self.send('VPS:{}'.format(mode), "VPS:{}{:02X}{:02X}", [ mode,self.position_x,self.position_y ]) - - # wipe / mix level - def set_mix(self, value): - self.send('VMM', "VMM:{:02X}", [ int(255*value) ]) + commands = { + 'colour_gain_T': { + 'name': 'Colour Corrector gain - both', + 'queue': 'VCG', + 'form': 'VCG:T{:02X}', + 'arg_names': [ 'v' ], + 'arguments': { 'v': 127 } + }, + 'colour_T': { + 'name': 'Colour Corrector - both', + 'queue': 'VCC', + 'form': 'VCC:T{:02X}{:02X}', + 'arg_names': [ 'x', 'y' ], + 'arguments': { 'x': 127, 'y': 127 }, + 'modulation': [ {}, {}, { 'x': 1.0 }, { 'y': 1.0 } ] + #'callback': self.set_colour + }, + 'mix': { + 'name': 'Mix/wipe', + 'queue': 'VMM', + 'form': 'VMM:{:02X}', + 'arg_names': [ 'v' ], + 'arguments': { 'v': 127 }, + 'modulation': [ { 'v': 1.0 }, {}, {}, {} ] + }, + 'back_colour': { + 'name': 'Back colour/matte HSV', + 'queue': 'VBM', + 'form': 'VBM:{:02X}{:02X}{:02X}', + 'arg_names': [ 'h', 's', 'v' ], + 'arguments': { 'h': 127, 's': 127, 'v': 127 }, + 'modulation': [ {}, { 'h': 1.0 }, {}, {} ] + }, + 'position_N': { + 'name': 'Positioner joystick', + 'queue': 'VPS', + 'form': 'VPS:N{:02X}{:02X}', + 'arg_names': [ 'y', 'x' ], + 'arguments': { 'y': 127, 'x': 127 } + }, + 'dsk_level': { + 'name': 'Downstream Key level', + 'queue': 'VDL', + 'form': 'VDL:{:02X}', + 'arg_names': [ 'v' ], + 'arguments': { 'v': 127 } + } + } + command_by_queue = {}