Overhaul of WJSendPlugin to define parameters programmatically and so enable modulation

This commit is contained in:
Tristan Rowley
2020-02-22 00:07:21 +00:00
parent 2bbe5a57ec
commit a77367f9d3
2 changed files with 121 additions and 98 deletions

View File

@@ -42,27 +42,27 @@
"control_change 48": { "control_change 48": {
"DEFAULT": ["set_the_shader_param_0_layer_offset_0_continuous","set_strobe_amount_continuous"], "DEFAULT": ["set_the_shader_param_0_layer_offset_0_continuous","set_strobe_amount_continuous"],
"NAV_DETOUR": ["set_detour_speed_position_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_MANI": ["set_variable_A"],
"NAV_LFO": ["set_lfo_modulation_0_level"] "NAV_LFO": ["set_lfo_modulation_0_level"]
}, },
"control_change 49": { "control_change 49": {
"DEFAULT": ["set_the_shader_param_1_layer_offset_0_continuous","set_shader_speed_layer_0_amount"], "DEFAULT": ["set_the_shader_param_1_layer_offset_0_continuous","set_shader_speed_layer_0_amount"],
"NAV_DETOUR": ["set_detour_start_continuous"], "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_MANI": ["f:sin(x*pi):|set_variable_SIN"],
"NAV_LFO": ["set_lfo_modulation_1_level"] "NAV_LFO": ["set_lfo_modulation_1_level"]
}, },
"control_change 50": { "control_change 50": {
"DEFAULT": ["set_the_shader_param_2_layer_offset_0_continuous","set_shader_speed_layer_1_amount"], "DEFAULT": ["set_the_shader_param_2_layer_offset_0_continuous","set_shader_speed_layer_1_amount"],
"NAV_DETOUR": ["set_detour_end_continuous"], "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"] "NAV_LFO": ["set_lfo_modulation_2_level"]
}, },
"control_change 51": { "control_change 51": {
"DEFAULT": ["set_the_shader_param_3_layer_offset_0_continuous","set_shader_speed_layer_2_amount"], "DEFAULT": ["set_the_shader_param_3_layer_offset_0_continuous","set_shader_speed_layer_2_amount"],
"NAV_DETOUR": ["set_detour_end_continuous"], "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"] "NAV_LFO": ["set_lfo_modulation_3_level"]
}, },
"control_change 52": { "control_change 52": {
@@ -74,17 +74,17 @@
"control_change 53": { "control_change 53": {
"DEFAULT": ["set_the_shader_param_1_layer_offset_1_continuous","set_param_1_layer_offset_0_modulation_level_continuous"], "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_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": { "control_change 54": {
"DEFAULT": ["set_the_shader_param_2_layer_offset_1_continuous","set_param_2_layer_offset_0_modulation_level_continuous"], "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_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": { "control_change 55": {
"DEFAULT": ["set_the_shader_param_3_layer_offset_1_continuous","set_param_3_layer_offset_0_modulation_level_continuous"], "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_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": { "control_change 56": {
"DEFAULT": ["set_the_shader_param_0_layer_offset_2_continuous"], "DEFAULT": ["set_the_shader_param_0_layer_offset_2_continuous"],

View File

@@ -6,7 +6,7 @@ import threading
class WJSendPlugin(ActionsPlugin, SequencePlugin, DisplayPlugin, ModulationReceiverPlugin, AutomationSourcePlugin): class WJSendPlugin(ActionsPlugin, SequencePlugin, DisplayPlugin, ModulationReceiverPlugin, AutomationSourcePlugin):
disabled = False#True disabled = False#True
DEBUG = False#True DEBUG = False #True
ser = None ser = None
# from http://depot.univ-nc.nc/sources/boxtream-0.9999/boxtream/switchers/panasonic.py # from http://depot.univ-nc.nc/sources/boxtream-0.9999/boxtream/switchers/panasonic.py
"""serial.Serial(device, baudrate=9600, """serial.Serial(device, baudrate=9600,
@@ -17,18 +17,6 @@ class WJSendPlugin(ActionsPlugin, SequencePlugin, DisplayPlugin, ModulationRecei
rtscts=True, # TODO : test without this one rtscts=True, # TODO : test without this one
timeout=timeout)""" 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 THROTTLE = 1 # milliseconds to wait between refreshing parameters
def __init__(self, plugin_collection): def __init__(self, plugin_collection):
@@ -38,6 +26,9 @@ class WJSendPlugin(ActionsPlugin, SequencePlugin, DisplayPlugin, ModulationRecei
print ("WJSendPlugin is disabled, not opening serial") print ("WJSendPlugin is disabled, not opening serial")
return return
for cmd,struct in self.commands.items():
self.command_by_queue[struct['queue']] = struct
self.pc.actions.tk.after(500, self.refresh) self.pc.actions.tk.after(500, self.refresh)
# methods/vars for AutomationSourcePlugin # methods/vars for AutomationSourcePlugin
@@ -64,19 +55,31 @@ class WJSendPlugin(ActionsPlugin, SequencePlugin, DisplayPlugin, ModulationRecei
# methods for ModulationReceiverPlugin - receives changes to the in-built modulation levels (-1 to +1) # methods for ModulationReceiverPlugin - receives changes to the in-built modulation levels (-1 to +1)
# experimental & hardcoded ! # experimental & hardcoded !
# TODO: make this not hardcoded and configurable mapping modulation to parameters, preferably on-the-fly.. # 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): 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 # 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 self.DEBUG: print("||||| WJSendPlugin received set_modulation_value for param %s with value %s!" % (param, value))
if param==0: #v = (0.5+value)/2
self.set_mix((0.5+value)/2) """mapped = [
elif param==1: 'mix',
self.set_colour('T', 'x', (0.5+value)/2) 'colour_T',
elif param==2: #'colour_T',
self.set_colour('T', 'y', (0.5+value)/2) 'back_colour:x'
elif param==3: ]"""
self.set_back_colour('x', (0.5+value)/2) #self.catch_all(*mapped[param].split(":")+[v])
else: #self.commands[
if self.DEBUG: print("\tunknown param %s!" % param) # 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 #methods for DisplayPlugin
def show_plugin(self, display, display_mode): def show_plugin(self, display, display_mode):
@@ -135,6 +138,7 @@ class WJSendPlugin(ActionsPlugin, SequencePlugin, DisplayPlugin, ModulationRecei
try: try:
# sorting the commands that are sent seems to fix jerk and lag that is otherwise pretty horrendous # 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()): for queue, command in sorted(self.queue.items()):
# TODO: modulate the parameters
self.send_buffered(queue, command[0], command[1]) self.send_buffered(queue, command[0], command[1])
#self.queue.clear() #self.queue.clear()
except Exception: except Exception:
@@ -154,9 +158,11 @@ class WJSendPlugin(ActionsPlugin, SequencePlugin, DisplayPlugin, ModulationRecei
last = {} last = {}
def send_buffered(self, queue, form, args, record = True): def send_buffered(self, queue, form, args, record = True):
# only send if new command is different to the last one we sent # 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)) #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.send_serial_string(output)
self.last[queue] = (form,args) self.last[queue] = (form,args)
if record: if record:
@@ -176,74 +182,91 @@ class WJSendPlugin(ActionsPlugin, SequencePlugin, DisplayPlugin, ModulationRecei
def parserlist(self): def parserlist(self):
return [ return [
( r"^open_serial$", self.open_serial ), ( r"^open_serial$", self.open_serial ),
( r"^wj_send_serial_([0-9a-zA-Z:]*)$", self.send_serial_string ), ( 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_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_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_position_([N|L])_([x|y])$", self.set_position ), #( r"^wj_set_mix$", self.set_mix ),
( 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_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_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 def catch_all(self, param, argument_name, value):
# TODO: come up with a way to represent this programmatically #print ("got catch-all %s, %s, %s" % (param, argument_name, value))
# TODO: add more! #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 def modulate_arguments(self, command, args):
colour_y = 127 args = args.copy()
def set_colour(self, chan, dim, value): if self.DEBUG: print("modulate_arguments passed %s and %s" % (command,args))
# chan can be A, B or T (both) for slot in range(0,4):
if dim=='x': modlevels = command.get('modulation',[{}]*4)[slot]
self.colour_x = int(255*value) if self.DEBUG: print("\tfor modulate_arguments for slot %s got modlevels: %s" % (slot, modlevels))
elif dim=='y': #if len(command.get('modulation',[{}]*4)[slot])>0:
self.colour_y = int(255*value) 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! commands = {
back_colour_x = 127 'colour_gain_T': {
back_colour_y = 127 'name': 'Colour Corrector gain - both',
back_colour_z = 127 'queue': 'VCG',
def set_back_colour(self, dim, value): 'form': 'VCG:T{:02X}',
# chan can be A, B or T (both) 'arg_names': [ 'v' ],
if dim=='x': 'arguments': { 'v': 127 }
self.back_colour_x = int(255*value) },
elif dim=='y': 'colour_T': {
self.back_colour_y = int(255*value) 'name': 'Colour Corrector - both',
elif dim=='z': 'queue': 'VCC',
self.back_colour_z = int(255*value) 'form': 'VCC:T{:02X}{:02X}',
'arg_names': [ 'x', 'y' ],
self.send('VBM', "VBM:{:02X}{:02X}{:02X}", [ self.back_colour_x,self.back_colour_y,self.back_colour_z ]) 'arguments': { 'x': 127, 'y': 127 },
'modulation': [ {}, {}, { 'x': 1.0 }, { 'y': 1.0 } ]
# this doesnt seem to work on WJ-MX30 at least, or maybe i dont know how to get it into the right mode? #'callback': self.set_colour
# TODO: replace with downstream key control },
back_wash_colour_x = 127 'mix': {
back_wash_colour_y = 127 'name': 'Mix/wipe',
back_wash_colour_z = 127 'queue': 'VMM',
def set_back_wash_colour(self, dim, value): 'form': 'VMM:{:02X}',
# chan can be A, B or T (both) 'arg_names': [ 'v' ],
if dim=='x': 'arguments': { 'v': 127 },
self.back_wash_colour_x = int(255*value) 'modulation': [ { 'v': 1.0 }, {}, {}, {} ]
elif dim=='y': },
self.back_wash_colour_y = int(255*value) 'back_colour': {
elif dim=='z': 'name': 'Back colour/matte HSV',
self.back_wash_colour_z = int(255*value) 'queue': 'VBM',
'form': 'VBM:{:02X}{:02X}{:02X}',
self.send('VBW', "VBW:{:02X}{:02X}{:02X}", [ self.back_wash_colour_x,self.back_wash_colour_y,self.back_wash_colour_z ] ) 'arg_names': [ 'h', 's', 'v' ],
'arguments': { 'h': 127, 's': 127, 'v': 127 },
# positioner joystick 'modulation': [ {}, { 'h': 1.0 }, {}, {} ]
position_x = 127 },
position_y = 127 'position_N': {
def set_position(self, mode, dim, value): 'name': 'Positioner joystick',
if dim=='y': # yes, y is really x! 'queue': 'VPS',
self.position_x = int(255*value) 'form': 'VPS:N{:02X}{:02X}',
elif dim=='x': # yes, x is really y! 'arg_names': [ 'y', 'x' ],
self.position_y = int(255*value) 'arguments': { 'y': 127, 'x': 127 }
},
self.send('VPS:{}'.format(mode), "VPS:{}{:02X}{:02X}", [ mode,self.position_x,self.position_y ]) 'dsk_level': {
'name': 'Downstream Key level',
# wipe / mix level 'queue': 'VDL',
def set_mix(self, value): 'form': 'VDL:{:02X}',
self.send('VMM', "VMM:{:02X}", [ int(255*value) ]) 'arg_names': [ 'v' ],
'arguments': { 'v': 127 }
}
}
command_by_queue = {}