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) ---- diff --git a/actions.py b/actions.py index 512d5a7..88a322f 100644 --- a/actions.py +++ b/actions.py @@ -175,6 +175,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) @@ -1019,6 +1034,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): diff --git a/data_centre/data.py b/data_centre/data.py index 1f0d797..913ebb6 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 @@ -92,7 +93,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/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: diff --git a/json_objects/midi_action_mapping_APC Key 25.json b/json_objects/midi_action_mapping_APC Key 25.json index c2c65cb..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"], @@ -152,7 +153,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"] @@ -282,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 9b2f6d1..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 @@ -91,13 +94,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 +111,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: + 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.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)) + + + + 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 +148,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 @@ -142,8 +162,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) diff --git a/plugins/WJSendPlugin.py b/plugins/WJSendPlugin.py index 746f7c8..da4f2ec 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))] @@ -274,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' @@ -324,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)) @@ -366,7 +373,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', diff --git a/user_input/numpad_input.py b/user_input/numpad_input.py index 845697d..db17e30 100644 --- a/user_input/numpad_input.py +++ b/user_input/numpad_input.py @@ -21,11 +21,15 @@ 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() - 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)) @@ -35,8 +39,13 @@ 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': + 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 @@ -58,6 +67,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: @@ -91,7 +104,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 @@ -104,7 +117,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 ):