From 9a2663453ead32e3754aa3458d9c75e951bfba1b Mon Sep 17 00:00:00 2001 From: Tristan Rowley Date: Sat, 29 Feb 2020 16:26:25 +0000 Subject: [PATCH 01/11] Fix for WJSend when missing or broken existing configs --- plugins/WJSendPlugin.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/plugins/WJSendPlugin.py b/plugins/WJSendPlugin.py index 746f7c8..c7eb4c8 100644 --- a/plugins/WJSendPlugin.py +++ b/plugins/WJSendPlugin.py @@ -53,7 +53,7 @@ class WJSendPlugin(ActionsPlugin, SequencePlugin, DisplayPlugin, ModulationRecei def save_presets(self): for cmd,struct in self.commands.items(): - self.presets.setdefault('modulation_levels',{})[cmd] = struct.get('modulation',[]) + self.presets.setdefault('modulation_levels',{})[cmd] = struct.get('modulation',[{},{},{},{}]) self.pc.update_json(self.PRESET_FILE_NAME, self.presets) def quit_plugin(self): @@ -106,9 +106,10 @@ class WJSendPlugin(ActionsPlugin, SequencePlugin, DisplayPlugin, ModulationRecei # so that they update with the new modulation value to_send = {} for queue,cmd in sorted(self.command_by_queue.items(),reverse=True): - 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: + cmd.setdefault('modulation',[{},{},{},{}]) + + if self.DEBUG: print("\tparam %s, checking modulation %s" % (param, cmd.get('modulation'))) + if len(cmd['modulation'][param])>0: if self.DEBUG: print("\tParam %s has modulation! sending update of values? %s" % (param, [cmd['arguments'][y] for y in cmd['arg_names'] ])) #self.send_buffered(cmd['queue'], cmd['form'], [x for x in [ cmd['arguments'][y] for y in cmd['arg_names'] ] ], record=False) @@ -138,7 +139,7 @@ class WJSendPlugin(ActionsPlugin, SequencePlugin, DisplayPlugin, ModulationRecei for arg_name in cmd['arg_names']: indicator = " " if cmd['arg_names'].index(arg_name)!=self.selected_argument_index else ">" output += "%s%s: "%(indicator,arg_name) - for slot,mods in enumerate(cmd.get('modulation',[{},{},{},{}])): + for slot,mods in enumerate(cmd.setdefault('modulation',[{},{},{},{}])): #if arg_name in mods: v = mods.get(arg_name,0.0) g = '%s'%bar[int(v*(len(bar)-1))] @@ -366,7 +367,7 @@ class WJSendPlugin(ActionsPlugin, SequencePlugin, DisplayPlugin, ModulationRecei 'form': 'VMM:{:02X}', 'arg_names': [ 'v' ], 'arguments': { 'v': 127 }, - 'modulation': [ { 'v': 1.0 }, {}, {}, {} ] + #'modulation': [ { 'v': 1.0 }, {}, {}, {} ] }, 'back_colour': { 'name': 'Back colour/matte HSV', From 056d60947f3b4967cebdb5d7eee3a295578eaae8 Mon Sep 17 00:00:00 2001 From: langolierz Date: Sat, 29 Feb 2020 17:26:39 +0100 Subject: [PATCH 02/11] Update README.md --- dotfiles/README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dotfiles/README.md b/dotfiles/README.md index b77fe58..e070726 100644 --- a/dotfiles/README.md +++ b/dotfiles/README.md @@ -209,7 +209,10 @@ sudo cp -R * /usr/local/ - install node-osc `npm install node-osc --save` (do we need this ?) - install osc-js `npm install node-osc --save` -install serial package : `pip3 install pyserial` +### install some plugin packages: + +- install serial package : `pip3 install pyserial` +- install pyaudio : `sudo apt-get install python3-pyaudio` # wjhat follows is info, not instructions for setup: From 37749ffb08a7e54ab1d878098e5f9d67412c096a Mon Sep 17 00:00:00 2001 From: Tristan Rowley Date: Sat, 29 Feb 2020 17:39:57 +0000 Subject: [PATCH 03/11] wj_reset_modulation action to reset all modulation levels set in WJSendPlugin. --- json_objects/midi_action_mapping_APC Key 25.json | 3 ++- plugins/WJSendPlugin.py | 8 +++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/json_objects/midi_action_mapping_APC Key 25.json b/json_objects/midi_action_mapping_APC Key 25.json index c2c65cb..e841bc4 100644 --- a/json_objects/midi_action_mapping_APC Key 25.json +++ b/json_objects/midi_action_mapping_APC Key 25.json @@ -152,7 +152,8 @@ "DEFAULT": ["switch_to_preset_7","select_preset_7"] }, "note_on 81": { - "DEFAULT": ["","reset_selected_modulation"] + "DEFAULT": ["","reset_selected_modulation"], + "NAV_WJMX": ["","wj_reset_modulation"] }, "note_on 82": { "DEFAULT": ["toggle_shader_layer_0","select_shader_modulation_slot_0"] diff --git a/plugins/WJSendPlugin.py b/plugins/WJSendPlugin.py index c7eb4c8..da4f2ec 100644 --- a/plugins/WJSendPlugin.py +++ b/plugins/WJSendPlugin.py @@ -275,9 +275,14 @@ class WJSendPlugin(ActionsPlugin, SequencePlugin, DisplayPlugin, ModulationRecei ( r"^wj_select_next_command$", self.select_next_command ), ( r"^wj_select_previous_command$", self.select_previous_command ), ( r"^wj_select_next_argument$", self.select_next_argument ), - ( r"^wj_select_previous_argument$", self.select_previous_argument ) + ( r"^wj_select_previous_argument$", self.select_previous_argument ), + ( r"^wj_reset_modulation$", self.reset_modulation_levels ) ] + def reset_modulation_levels(self): + for cmd,struct in self.commands.items(): + struct['modulation'] = [{},{},{},{}] + def set_modulation_command_argument_level(self, command_name, argument_name, slot, level): if self.DEBUG: print("set_modulation_command_argument_level(%s, %s, %s, %s)" % (command_name, argument_name, slot, level)) if not argument_name: self.commands[command_name]['arg_names'][0] #argument_name = 'v' @@ -325,6 +330,7 @@ class WJSendPlugin(ActionsPlugin, SequencePlugin, DisplayPlugin, ModulationRecei def modulate_arguments(self, command, args): args = args.copy() #if self.DEBUG: print("modulate_arguments passed %s and\n\t%s" % (command,args)) + # TODO: rewrite this so that it combines multiple inputs and averages them for slot in range(0,4): modlevels = command.get('modulation',[{},{},{},{}])[slot] #if self.DEBUG: print("\tfor modulate_arguments for slot %s got modlevels: %s" % (slot, modlevels)) From 75c149338cc6e35b5fb9d50a18506f37a5f63663 Mon Sep 17 00:00:00 2001 From: Tristan Rowley Date: Sat, 29 Feb 2020 17:41:35 +0000 Subject: [PATCH 04/11] updated ACTIONS --- ACTIONS.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ACTIONS.md b/ACTIONS.md index 989c717..a08d405 100644 --- a/ACTIONS.md +++ b/ACTIONS.md @@ -1,6 +1,6 @@ # Auto-generated Actions list -Sat 29 Feb 16:00:23 UTC 2020 +Sat 29 Feb 17:41:32 UTC 2020 for branch=feature_plugins @@ -238,6 +238,7 @@ for branch=feature_plugins * wj_select_previous_command (from WJSendPlugin) * wj_select_next_argument (from WJSendPlugin) * wj_select_previous_argument (from WJSendPlugin) + * wj_reset_modulation (from WJSendPlugin) ---- From 6d28785d08f61123c113600bdbf5b80f1674c84d Mon Sep 17 00:00:00 2001 From: Tristan Rowley Date: Sat, 29 Feb 2020 18:00:39 +0000 Subject: [PATCH 05/11] 'peakfreq' sends sane values now (even if they are still nonsense) --- plugins/SoundReactPlugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/SoundReactPlugin.py b/plugins/SoundReactPlugin.py index 9b2f6d1..701ffaa 100644 --- a/plugins/SoundReactPlugin.py +++ b/plugins/SoundReactPlugin.py @@ -142,8 +142,8 @@ class SoundReactPlugin(ActionsPlugin,SequencePlugin,DisplayPlugin): freqPeak = freq[np.where(fft==np.max(fft))[0][0]]+1 if freqPeak<400: return False - value = freqPeak/16000 - value = value**16/16 + value = freqPeak/2000 # ? + #value = (value**16) if self.DEBUG: print("peak frequency:\t%d\tHz\t(converted to %s)"%(freqPeak,value)) self.display_values['peakfreq'] = ("%d Hz\t"%freqPeak) + "{:03.2f}".format(value) From 2455df0f438749075a6f207fdfceadbc52dfe606 Mon Sep 17 00:00:00 2001 From: Tristan Rowley Date: Sat, 29 Feb 2020 19:02:14 +0000 Subject: [PATCH 06/11] "set_display_mode_settings" etc to switch display screen, thanks to https://github.com/sebiik for the idea --- actions.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/actions.py b/actions.py index 8b2d439..858ea52 100644 --- a/actions.py +++ b/actions.py @@ -160,6 +160,21 @@ class Actions(object): if self.video_driver.current_player.show_toggle_on == self.video_driver.next_player.show_toggle_on: self.video_driver.next_player.toggle_show() + def set_display_mode(self, display_mode): + mapmodes = { + 'shader': 'SHADERS', + 'shader_bank': 'SHDR_BNK' + } + if display_mode in mapmodes: + display_mode = mapmodes[display_mode] + + display_mode = display_mode.upper() + + display_modes = self.data.get_display_modes_list(with_nav_mode=True) + for i,dm in enumerate(display_modes): + if display_mode in dm: + self.data.display_mode = display_modes[i][0] + self.data.control_mode = display_modes[i][1] def cycle_display_mode(self): display_modes = self.data.get_display_modes_list(with_nav_mode=True) @@ -1004,6 +1019,7 @@ class Actions(object): ( r"^select_shader_modulation_slot_([0-3])$", self.shaders.select_shader_modulation_slot ), ( r"^set_shader_speed_layer_offset_([0-2])_amount$", self.shaders.set_speed_offset_to_amount ), ( r"^set_shader_speed_layer_([0-2])_amount$", self.shaders.set_speed_layer_to_amount ), + ( r"^set_display_mode_([a-zA-Z_]*)$", self.set_display_mode ) } def detect_types(self, args): From 952fbcc941dc461e05b686fbc28f931d73e2c6e8 Mon Sep 17 00:00:00 2001 From: Tristan Rowley Date: Sat, 29 Feb 2020 19:22:43 +0000 Subject: [PATCH 07/11] fast-switch between display modes by holding DISP and pressing number keys -- doesn't work for 4 and 9 currently though?! --- data_centre/data.py | 1 + user_input/numpad_input.py | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/data_centre/data.py b/data_centre/data.py index 0dce385..eb3751e 100644 --- a/data_centre/data.py +++ b/data_centre/data.py @@ -57,6 +57,7 @@ class Data(object): ### state data self.auto_repeat_on = True self.function_on = False + self.is_display_held = False self.display_mode = "SAMPLER" self.control_mode = 'PLAYER' self.bank_number = 0 diff --git a/user_input/numpad_input.py b/user_input/numpad_input.py index 845697d..43a47a2 100644 --- a/user_input/numpad_input.py +++ b/user_input/numpad_input.py @@ -21,10 +21,17 @@ class NumpadInput(object): def on_key_press(self, event): numpad = list(string.ascii_lowercase[0:19]) + if event.char is 'h': # DISP button + self.data.is_display_held = True + if event.char is '.' or event.char is 'z': self.actions.quit_the_program() if event.char is 's': event.char = self.on_0_key_press() + + numbers = "jklmnopqrs" + if self.data.is_display_held and event.char in numbers: + self.actions.call_method_name("set_display_mode_%s"%self.data.get_display_modes_list()[numbers.index(event.char)]) elif event.char in numpad: self.run_action_for_mapped_key(event.char) else: @@ -35,6 +42,9 @@ class NumpadInput(object): if event.char in numpad: self.check_key_release_settings(event.char) + if event.char is 'h': + self.data.is_display_held = False + def on_mouse_move(self, event): if self.data.settings['user_input']['MOUSE_INPUT']['value'] != 'enabled': return From 5ef24c881fa8dc3c8054561827a3f3af96d10d77 Mon Sep 17 00:00:00 2001 From: Tristan Rowley Date: Sat, 29 Feb 2020 21:58:57 +0000 Subject: [PATCH 08/11] fix for 4+9 not working for switching display mode --- user_input/numpad_input.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/user_input/numpad_input.py b/user_input/numpad_input.py index 43a47a2..77d70da 100644 --- a/user_input/numpad_input.py +++ b/user_input/numpad_input.py @@ -29,10 +29,7 @@ class NumpadInput(object): if event.char is 's': event.char = self.on_0_key_press() - numbers = "jklmnopqrs" - if self.data.is_display_held and event.char in numbers: - self.actions.call_method_name("set_display_mode_%s"%self.data.get_display_modes_list()[numbers.index(event.char)]) - elif event.char in numpad: + if event.char in numpad: self.run_action_for_mapped_key(event.char) else: print('{} is not in keypad map'.format(event.char)) @@ -68,6 +65,10 @@ class NumpadInput(object): is_function = 1 else: is_function = 0 + + numbers = "jklmnopqrs" + if self.data.is_display_held and key in numbers: + return self.actions.call_method_name("set_display_mode_%s"%self.data.get_display_modes_list()[numbers.index(key)]) print('the numpad action being called is {} (mode is {})'.format(this_mapping[mode][is_function], mode)) if value != -1: @@ -101,7 +102,7 @@ class NumpadInput(object): if(not self.in_0_event ): self.in_0_event = True self.additional_0_in_event = 0 - self.root.after(600, self.check_event_outcome) + self.root.after(60, self.check_event_outcome) else: self.additional_0_in_event = self.additional_0_in_event + 1 @@ -114,7 +115,7 @@ class NumpadInput(object): self.run_action_for_mapped_key('n') elif(self.additional_0_in_event == 1): print('this doesnt happen - may not be needed') - self.root.after(600, self.second_check_event_outcome) + self.root.after(60, self.second_check_event_outcome) def second_check_event_outcome(self): if(self.additional_0_in_event == 1 ): From 0727684a0c8e5e4d313cfb527cdb52d5771f0a33 Mon Sep 17 00:00:00 2001 From: Tristan Rowley Date: Sun, 1 Mar 2020 15:21:43 +0000 Subject: [PATCH 09/11] wip: trigger changes when peak level changse significantly from the average --- .../midi_action_mapping_APC Key 25.json | 5 +++-- plugins/SoundReactPlugin.py | 21 +++++++++++++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/json_objects/midi_action_mapping_APC Key 25.json b/json_objects/midi_action_mapping_APC Key 25.json index e841bc4..71ac2a8 100644 --- a/json_objects/midi_action_mapping_APC Key 25.json +++ b/json_objects/midi_action_mapping_APC Key 25.json @@ -60,7 +60,8 @@ "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_LFO": ["set_lfo_modulation_2_level"] + "NAV_LFO": ["set_lfo_modulation_2_level"], + "NAV_SND": ["sound_set_config_energy_triggerthreshold"] }, "control_change 51": { "DEFAULT": ["set_the_shader_param_3_layer_offset_0_continuous","set_shader_speed_layer_2_amount"], @@ -283,7 +284,7 @@ "DEFAULT": ["send_serial_macro_0","send_serial_macro_1"] }, "note_on 93": { - "DEFAULT": ["smooth_selected_clip","send_random_settings"] + "DEFAULT": ["set_display_mode_settings","set_display_mode_shader"] } } diff --git a/plugins/SoundReactPlugin.py b/plugins/SoundReactPlugin.py index 701ffaa..d08a8b6 100644 --- a/plugins/SoundReactPlugin.py +++ b/plugins/SoundReactPlugin.py @@ -91,13 +91,14 @@ class SoundReactPlugin(ActionsPlugin,SequencePlugin,DisplayPlugin): #display.display_text.insert(END, "\nLevels:%s\n\n" % self.levels) display.display_text.insert(END, "\n\n\n") - + energy_history = [] def run_sequence(self, position): # position is irrelvant for this plugin, we just want to run continuously if not self.active: return data = np.fromstring(self.stream.read(self.CHUNK, exception_on_overflow = False),dtype=np.int16) + previous_value = {} for sourcename in self.sources: value = self.sources[sourcename](data) @@ -107,8 +108,24 @@ class SoundReactPlugin(ActionsPlugin,SequencePlugin,DisplayPlugin): for slot,level in enumerate(self.levels.get(sourcename,[])): if level>0.0 and self.values.get(sourcename)!=self.last_values.get(sourcename): self.pc.actions.call_method_name("modulate_param_%s_to_amount_continuous"%slot, self.values[sourcename]) + previous_value[sourcename] = self.last_values.get(sourcename) or value self.last_values[sourcename] = self.values[sourcename] + if sourcename is 'energy' and self.last_values.get('energy') is not None: + from statistics import mean + diff = abs(self.last_values.get('energy',value)-previous_value.get(sourcename,value)) #mean(self.energy_history)) + if len(self.energy_history)>5: #self.duration: + meandiff = abs(diff-mean(self.energy_history[:int(len(self.energy_history)/2)])) + #print (" diff is %s, meandiff %s" % (diff, meandiff)) + if meandiff>=self.config['energy'].get('triggerthreshold',0.15): + self.energy_history = [] + print ("\n>>>>>>Triggering dynamic change for meandiff %s?\n" % meandiff) + self.energy_history.append(diff) #self.values.get(sourcename,0.0)) + #print("logging %s" % diff) #self.values.get(sourcename,0.0)) + + + + config.setdefault('energy',{})['gain'] = 0.5 # how much to multiply signal by config.setdefault('energy',{})['threshold'] = 0.5 # subtract from post-gain signal (hence ignore all values below) GAIN_MULT = 1.0 @@ -128,7 +145,7 @@ class SoundReactPlugin(ActionsPlugin,SequencePlugin,DisplayPlugin): if self.DEBUG: print("energy:\t\t%05d %s\t(converted to %s)"%(peak,bars,value)) bar = u"_\u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588" g = '%s'%bar[int(value*(len(bar)-1))] - self.display_values['energy'] = "{} g{:03.2f} t{:03.2f}".format(g, self.config['energy']['gain'], self.config['energy']['threshold']) + self.display_values['energy'] = "{} g{:03.2f} t{:03.2f} d{:03.2f}".format(g, self.config['energy']['gain'], self.config['energy']['threshold'], self.config['energy'].setdefault('triggerthreshold',0.15)) return value From 452280805c2e2729a9eee715a2e90e47ee15a1b6 Mon Sep 17 00:00:00 2001 From: Tristan Rowley Date: Sun, 1 Mar 2020 15:48:17 +0000 Subject: [PATCH 10/11] more wip --- plugins/SoundReactPlugin.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/SoundReactPlugin.py b/plugins/SoundReactPlugin.py index d08a8b6..c668380 100644 --- a/plugins/SoundReactPlugin.py +++ b/plugins/SoundReactPlugin.py @@ -4,6 +4,9 @@ from data_centre.plugin_collection import ActionsPlugin, SequencePlugin, Display import pyaudio import numpy as np +from random import randint +from statistics import mean + #import matplotlib.pyplot as plt np.set_printoptions(suppress=True) # don't use scientific notationn @@ -112,7 +115,6 @@ class SoundReactPlugin(ActionsPlugin,SequencePlugin,DisplayPlugin): self.last_values[sourcename] = self.values[sourcename] if sourcename is 'energy' and self.last_values.get('energy') is not None: - from statistics import mean diff = abs(self.last_values.get('energy',value)-previous_value.get(sourcename,value)) #mean(self.energy_history)) if len(self.energy_history)>5: #self.duration: meandiff = abs(diff-mean(self.energy_history[:int(len(self.energy_history)/2)])) @@ -120,6 +122,7 @@ class SoundReactPlugin(ActionsPlugin,SequencePlugin,DisplayPlugin): if meandiff>=self.config['energy'].get('triggerthreshold',0.15): self.energy_history = [] print ("\n>>>>>>Triggering dynamic change for meandiff %s?\n" % meandiff) + #self.pc.actions.call_method_name("load_slot_%s_into_next_player"%randint(0,9)) self.energy_history.append(diff) #self.values.get(sourcename,0.0)) #print("logging %s" % diff) #self.values.get(sourcename,0.0)) From d6fa0b81f775ecab95838e18e02b2df939bd1a05 Mon Sep 17 00:00:00 2001 From: Tristan Rowley Date: Sun, 1 Mar 2020 16:09:12 +0000 Subject: [PATCH 11/11] Don't barf on MOUSE_INPUT if it doesn't exist in config --- data_centre/data.py | 3 ++- user_input/numpad_input.py | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/data_centre/data.py b/data_centre/data.py index 236e4db..dfd8fcb 100644 --- a/data_centre/data.py +++ b/data_centre/data.py @@ -69,7 +69,8 @@ class Data(object): self.shader_bank_data = [self.create_empty_shader_bank() for i in range(3)] if os.path.isfile(self.PATH_TO_DATA_OBJECTS + self.SHADER_BANK_DATA_JSON): self.shader_bank_data = self._read_json(self.SHADER_BANK_DATA_JSON) - self.settings = self.default_settings = self._read_json(self.DEFAULT_SETTINGS_JSON) + self.settings = self._read_json(self.DEFAULT_SETTINGS_JSON) + self.default_settings = self.settings.copy() if os.path.isfile(self.PATH_TO_DATA_OBJECTS + self.SETTINGS_JSON): self.settings = self._read_json(self.SETTINGS_JSON) diff --git a/user_input/numpad_input.py b/user_input/numpad_input.py index dd3c765..bada2cd 100644 --- a/user_input/numpad_input.py +++ b/user_input/numpad_input.py @@ -36,7 +36,9 @@ class NumpadInput(object): self.check_key_release_settings(event.char) def on_mouse_move(self, event): - if self.data.settings['user_input']['MOUSE_INPUT']['value'] != 'enabled': + if self.data.settings['user_input'].setdefault( + 'MOUSE_INPUT', + self.data.default_settings.get('MOUSE_INPUT',{'value': 'enabled'})).get('value') != 'enabled': return if event.x > 480 or event.y > 320: return