From 421e2dc6ed6fcd50c4e1fe9084a935c9df323708 Mon Sep 17 00:00:00 2001 From: Tristan Rowley Date: Wed, 12 Feb 2020 00:02:05 +0000 Subject: [PATCH] Revert "Merge branch 'dev_frame_objects' into feature_plugins" This reverts commit 9d5792e1c911c4a7aecc81f9f9f04bb3f2f264a8, reversing changes made to 44ab20cb5221bfbd73ce64e535031a5ef335e703. --- ACTIONS.md | 22 +-- README.md | 20 --- data_centre/plugin_collection.py | 15 +- plugins/ShaderLoopRecordPlugin.py | 199 --------------------- plugins/ShaderQuickPresetPlugin.py | 85 --------- plugins/TestPlugin.py | 4 +- plugins/frame_manager.py | 278 ----------------------------- video_centre/shaders.py | 2 + 8 files changed, 12 insertions(+), 613 deletions(-) delete mode 100644 plugins/ShaderLoopRecordPlugin.py delete mode 100644 plugins/ShaderQuickPresetPlugin.py delete mode 100644 plugins/frame_manager.py diff --git a/ACTIONS.md b/ACTIONS.md index e35dcb7..8187fab 100644 --- a/ACTIONS.md +++ b/ACTIONS.md @@ -1,8 +1,8 @@ # Auto-generated Actions list -Sun 9 Feb 16:03:51 UTC 2020 +Sun 9 Feb 15:57:04 UTC 2020 -for branch=feature_plugins_shader_gadgets +for branch=feature_plugins # Methods * change_composite_setting(setting_value) @@ -180,24 +180,6 @@ for branch=feature_plugins_shader_gadgets * ([A-Z0-9]+)>(.*) (from ManipulatePlugin) * (.*)>&(.*) (from ManipulatePlugin) * (.*)&&(.*) (from MultiActionsPlugin) - * run_automation (from ShaderLoopRecordPlugin) - * stop_automation (from ShaderLoopRecordPlugin) - * toggle_pause_automation (from ShaderLoopRecordPlugin) - * pause_automation (from ShaderLoopRecordPlugin) - * toggle_loop_automation (from ShaderLoopRecordPlugin) - * set_automation_speed (from ShaderLoopRecordPlugin) - * toggle_record_automation (from ShaderLoopRecordPlugin) - * toggle_overdub_automation (from ShaderLoopRecordPlugin) - * clear_automation (from ShaderLoopRecordPlugin) - * select_automation_clip_([0-7]) (from ShaderLoopRecordPlugin) - * toggle_automation_clip_([0-7]) (from ShaderLoopRecordPlugin) - * load_presets (from ShaderQuickPresetPlugin) - * save_presets (from ShaderQuickPresetPlugin) - * store_next_preset (from ShaderQuickPresetPlugin) - * store_current_preset (from ShaderQuickPresetPlugin) - * switch_to_preset_([0-%i]) (from ShaderQuickPresetPlugin) - * select_preset_([0-%i]) (from ShaderQuickPresetPlugin) - * clear_current_preset (from ShaderQuickPresetPlugin) * test_plugin (from TestPlugin) * cycle_shaders (from TestPlugin) * run_automation (from TestPlugin) diff --git a/README.md b/README.md index bfcacdd..1a00df5 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,3 @@ -This is doctea's experimental 'plugins' branch, including 'shader gadget' plugins. - -Started adding some documentation for some of these features on the wiki here: -* https://github.com/langolierz/r_e_c_u_r/wiki/Sound-reactivity -* https://github.com/langolierz/r_e_c_u_r/wiki/using-the-modulation-parameters -* https://github.com/langolierz/r_e_c_u_r/wiki/Plugins - -Some quick notes, including about how the modulation features work:- - -* Plugins are python classes... With adaptations to recur main code to look up actions in plugins of ActionPlugin class, so plugins can provide extra actions that can be mapped to midi/keyboard/osc -* Here's the really primitive soundreact app: https://github.com/doctea/r_e_c_u_r/blob/feature_midi_feedback_plugin/helpers/soundreact.py -* All it does is send OSC messages based on the volume (the threshold stuff doesn't work and isn't actually activated) -* So then if you look in https://github.com/doctea/r_e_c_u_r/blob/feature_midi_feedback_plugin/json_objects/osc_action_mapping.json you'll see that the "/volume" osc address is bound to the set_modulation_...._value action -* So that way the modulation value is set for 'mod slot 1' to the volume value -* Then when recur is sending paramwter values to the conjur side, it calculates the value to send based on the mod slot level (changed using set_modulation..._level actions mapped in midi APC key25 JSON) and mod slot values -* So you there's 4 modulation parameters and you can assign them, with level, to any of the shader parameters as desired, and feed in params from any source into each mod slot -* At moment there's 3 types of plugin which can also be combined: midifeedback for showing leds etc on controllers that support it like APC Key 25 and Launchpad to indicate recur status, actions for adding new actions, and sequences for running short or looped sequences and adjusting speed etc -* Demo useful plugins are here in this shader gadgets branch, demonstrating shader presets and automation recording - -Any questions ask me doctea@gmail.com, find me on facebook or open an issue or something :) # r_e_c_u_r diff --git a/data_centre/plugin_collection.py b/data_centre/plugin_collection.py index d162097..56dd144 100644 --- a/data_centre/plugin_collection.py +++ b/data_centre/plugin_collection.py @@ -3,7 +3,6 @@ import os import pkgutil import re -from plugins.frame_manager import FrameManager, Frame class Plugin(object): """Base class that each plugin must inherit from. within this class @@ -211,16 +210,16 @@ class DisplayPlugin(Plugin): def __init__(self, plugin_collection): super().__init__(plugin_collection) - def is_handled(self, name): + def is_handled(self, name): raise NotImplementedError - def get_display_modes(self): + def get_display_modes(self): raise NotImplementedError - def show_plugin(self, display): - from tkinter import Text, END - #display_text.insert(END, 'test from DisplayPlugin') - display.display_text.insert(END, '{} \n'.format(display.body_title)) + def show_plugin(self, display): + from tkinter import Text, END + #display_text.insert(END, 'test from DisplayPlugin') + display.display_text.insert(END, '{} \n'.format(display.body_title)) class ModulationReceiverPlugin(Plugin): def __init__(self, plugin_collection): @@ -260,8 +259,6 @@ class PluginCollection(object): #self.actions = message_handler.actions self.reload_plugins() - self.fm = FrameManager(self) - def read_json(self, file_name): return self.data._read_plugin_json(file_name) def update_json(self, file_name, data): diff --git a/plugins/ShaderLoopRecordPlugin.py b/plugins/ShaderLoopRecordPlugin.py deleted file mode 100644 index c986610..0000000 --- a/plugins/ShaderLoopRecordPlugin.py +++ /dev/null @@ -1,199 +0,0 @@ -import data_centre.plugin_collection -from data_centre.plugin_collection import ActionsPlugin, SequencePlugin -from plugins.frame_manager import Frame - -class ShaderLoopRecordPlugin(ActionsPlugin,SequencePlugin): - disabled = False - MAX_CLIPS = 8 - frames = [] - - def __init__(self, plugin_collection): - super().__init__(plugin_collection) - - self.PRESET_FILE_NAME = "ShaderLoopRecordPlugin/frames.json" - - #TODO: this doesnt work - """self.frames = [ - [ - Frame(self.pc).store_copy(f) - for f in [ - z for z in [ - frame for frame in [ - clip for clip in self.load_presets() - ] - ] - ] - ] - ]""" - for clip in self.load_presets(): - c = [] - for frame in clip: - c.append(Frame(self.pc).store_copy(frame)) - self.frames.append(c) - - self.reset_ignored() - - def load_presets(self): - #try: - print("trying load presets? %s " % self.PRESET_FILE_NAME) - p = self.pc.read_json(self.PRESET_FILE_NAME) - if p: - while len(p) self.duration/self.frequency: - current_frame_index = self.duration/self.frequency - - if self.DEBUG_FRAMES: print (">>>>>>>>>>>>>>frame at %i%%: %i" % (position*100, current_frame_index)) - #print("got frame index %s" % current_frame_index) - - current_frame = self.pc.fm.get_live_frame() #.copy() - - selected_clip = self.selected_clip - if self.DEBUG_FRAMES: print("current_frame copy before recall is %s" % current_frame['shader_params']) - #print ("%s clips, looks like %s" % (len(self.frames),self.frames)) - - #print("selected_clip is %s "%selected_clip) - #clip = self.frames[selected_clip] - if self.is_playing() and self.recording and self.selected_clip not in self.running_clips: - self.running_clips += [ self.selected_clip ] - for selected_clip in self.running_clips: - saved_frame = self.frames[selected_clip][current_frame_index] - if not self.recording or (selected_clip!=self.selected_clip): - self.pc.fm.recall_frame(saved_frame) - if self.recording and selected_clip==self.selected_clip: - if self.last_frame is None: - self.last_frame = current_frame - if self.DEBUG_FRAMES: print("last frame is \t\t%s" % self.last_frame['shader_params']) - if self.DEBUG_FRAMES: print("current f is \t\t%s" % current_frame['shader_params']) - diff = self.pc.fm.get_frame_diff(self.last_frame,current_frame) - if self.DEBUG_FRAMES: print("diffed frame is \t%s" % diff['shader_params']) - - if self.overdub and saved_frame: - # add the params tweaked this frame to the params to be ignored by recall - if self.DEBUG_FRAMES: print("saved frame is \t%s" % saved_frame['shader_params']) - self.ignored = self.pc.fm.merge_frames(self.ignored, diff) - print("about to call get_ignored_frames with %s\n and\n %s" % (saved_frame.f, self.ignored.f)) - diff = self.pc.fm.merge_frames( - self.pc.fm.get_frame_ignored(saved_frame, self.ignored), - diff - ) - #diff = self.pc.shaders.merge_frames(self.pc.shaders.get_live_frame(), diff) - self.pc.fm.recall_frame(diff) - if self.DEBUG_FRAMES: print("after diff2 is: \t%s" % diff['shader_params']) - if self.DEBUG_FRAMES: print("||||saving frame \t%s" % (diff['shader_params'])) - self.frames[selected_clip][current_frame_index] = diff #self.get_frame_diff(self.last_frame,current_frame) - #backfill frames - if self.last_saved_index is not None: - if self.DEBUG_FRAMES: print ("last_saved_index is %s, current_frame_index is %s" % (self.last_saved_index, current_frame_index)) - for i in range(current_frame_index - (self.last_saved_index) ): - if self.DEBUG_FRAMES:print("backfilling frame %s" % ((self.last_saved_index+i+1)%len(self.frames[selected_clip]))) - self.frames[selected_clip][(self.last_saved_index+i+1)%len(self.frames[selected_clip])] = diff - self.last_saved_index = current_frame_index - self.last_frame = self.pc.fm.get_live_frame() #diff - if self.DEBUG_FRAMES: print("<<<<<<<<<<<<<< frame at %s" % current_frame_index) - - """def recall_frame_index(self, index): - self.pc.shaders.recall_frame_params(self.frames[index].copy())""" - diff --git a/plugins/ShaderQuickPresetPlugin.py b/plugins/ShaderQuickPresetPlugin.py deleted file mode 100644 index dfb359e..0000000 --- a/plugins/ShaderQuickPresetPlugin.py +++ /dev/null @@ -1,85 +0,0 @@ -import data_centre.plugin_collection -from data_centre.plugin_collection import ActionsPlugin, SequencePlugin -import copy -from plugins.frame_manager import Frame - -class ShaderQuickPresetPlugin(ActionsPlugin): #,SequencePlugin): - disabled = False - - MAX_PRESETS = 8 - - def __init__(self, plugin_collection): - super().__init__(plugin_collection) - self.PRESET_FILE_NAME = 'ShaderQuickPresetPlugin/presets.json' - - self.presets = self.load_presets() - print("loaded presets %s" % self.presets) - - self.selected_preset = None - self.last_recalled = None - - def load_presets(self): - print("trying load presets? %s " % self.PRESET_FILE_NAME) - return [ Frame(self.pc).store_copy(x) for x in (self.pc.read_json(self.PRESET_FILE_NAME) or ([None]*self.MAX_PRESETS)) ] - - def save_presets(self): - self.pc.update_json(self.PRESET_FILE_NAME, self.presets) - - @property - def parserlist(self): - return [ - ( r"load_presets", self.load_presets ), - ( r"save_presets", self.save_presets ), - ( r"store_next_preset", self.store_next_preset ), - ( r"store_current_preset", self.store_current_preset ), - ( r"switch_to_preset_([0-%i])"%self.MAX_PRESETS, self.switch_to_preset ), - ( r"select_preset_([0-%i])"%self.MAX_PRESETS, self.select_preset ), - ( r"clear_current_preset", self.clear_current_preset ), - ] - - def store_next_preset(self): - res = [i for i, val in enumerate(self.presets) if val == None] - if res is None or not res: - self.selected_preset += 1 - self.selected_preset %= self.MAX_PRESETS - else: - self.selected_preset = res[0] - - self.store_current_preset() - - def clear_current_preset(self): - if self.selected_preset is None: - return - self.presets[self.selected_preset] = None - - self.save_presets() - - def store_current_preset(self): - if self.selected_preset is None: self.selected_preset = 0 - - insert_position = self.selected_preset - self.presets[insert_position] = self.pc.fm.get_live_frame() - #print ("stored %s at position %s" % (self.presets[insert_position], insert_position)) - self.selected_preset = insert_position - self.last_recalled = insert_position - - self.save_presets() - - def select_preset(self, preset): - self.selected_preset = preset - - def switch_to_preset(self, preset): - #if preset>len(self.presets): - if self.presets[preset] is None: - print ("no quick shader preset in slot %s!" % preset) - self.selected_preset = preset - return - print ("switching to preset %s" % preset) - self.selected_preset = preset - - self.last_recalled = preset - preset = self.presets[preset] - - print ("recalled preset %s" % preset) - self.pc.fm.recall_frame(preset) - diff --git a/plugins/TestPlugin.py b/plugins/TestPlugin.py index a6ad99e..bf14a90 100644 --- a/plugins/TestPlugin.py +++ b/plugins/TestPlugin.py @@ -1,8 +1,8 @@ import data_centre.plugin_collection from data_centre.plugin_collection import ActionsPlugin, SequencePlugin -class MidiActionsTestPlugin(ActionsPlugin,SequencePlugin): - disabled = True +class TestPlugin(ActionsPlugin,SequencePlugin): + disabled = False def __init__(self, plugin_collection): super().__init__(plugin_collection) diff --git a/plugins/frame_manager.py b/plugins/frame_manager.py deleted file mode 100644 index 829f91d..0000000 --- a/plugins/frame_manager.py +++ /dev/null @@ -1,278 +0,0 @@ -import copy -import json -from json import JSONEncoder - -def _default(self, obj): - if getattr(obj.__class__,'to_json'): - #return _default.default(obj.to_json()) - return obj.to_json() - else: - return _default.default(obj) - -_default.default = JSONEncoder().default -JSONEncoder.default = _default - -class Frame: - f = { 'shader_params': [[None]*4,[None]*4,[None]*4] } - pc = None - - DEBUG_FRAMES = False#True - - def __init__(self, pc): - import copy #from copy import deepcopy - self.pc = pc - - def to_json(self): - return self.f #{ 'f': self.f } - - def store_live(self): - frame = { - 'selected_shader_slots': [ shader.get('slot',None) for shader in self.pc.shaders.selected_shader_list ], - 'shader_params': copy.deepcopy(self.pc.shaders.selected_param_list), - 'layer_active_status': copy.deepcopy(self.pc.shaders.selected_status_list), - 'feedback_active': self.pc.shaders.data.feedback_active, - 'x3_as_speed': self.pc.shaders.data.settings['shader']['X3_AS_SPEED']['value'], - 'shader_speeds': copy.deepcopy(self.pc.shaders.selected_speed_list), - 'strobe_amount': self.pc.shaders.data.settings['shader']['STROBE_AMOUNT']['value'] / 10.0 - } - self.f = frame - #print("built frame: %s" % frame['shader_params']) - return self - - def store_copy(self, f): - print("told to store_copy %s" % f) - if f is not None: - if f.get('f') is not None: #isinstance(f, Frame): - f = f.get('f') - return self.store_copy(f.get('f')) - self.f = f - else: - self.f = {} - return self - - def recall_frame_params(self): - #print("recall_frame_params got: %s" % preset.get('shader_params')) - for (layer, param_list) in enumerate(self.f.get('shader_params',[])): - if param_list: - for param,value in enumerate(param_list): - #if (ignored is not None and ignored['shader_params'][layer][param] is not None): - # print ("ignoring %s,%s because value is %s" % (layer,param,ignored['shader_params'][layer][param])) - # continue - if (value is not None): - #print("recalling layer %s param %s: value %s" % (layer,param,value)) - self.pc.actions.call_method_name('set_the_shader_param_%s_layer_%s_continuous' % (param,layer), value) - - if self.f.get('feedback_active') is not None: - self.pc.data.feedback_active = self.f.get('feedback_active',self.pc.data.feedback_active) - if self.pc.data.feedback_active: - self.pc.actions.call_method_name('enable_feedback') - else: - self.pc.actions.call_method_name('disable_feedback') - - if self.f.get('x3_as_speed') is not None: - self.pc.data.settings['shader']['X3_AS_SPEED']['value'] = self.f.get('x3_as_speed',self.pc.data.settings['shader']['X3_AS_SPEED']['value']) - """if self.data.settings['shader']['X3_AS_SPEED']['value']: - self.data.plugins.actions.call_method_name('enable_x3_as_speed') - else: - self.data.plugins.actions.call_method_name('disable_x3_as_speed')""" - - for (layer, speed) in enumerate(self.f.get('shader_speeds',[])): - if speed is not None: - self.pc.actions.call_method_name('set_shader_speed_layer_%s_amount' % layer, speed) - - if self.f.get('strobe_amount') is not None: - self.pc.actions.set_strobe_amount_continuous(self.f.get('strobe_amount')) - - def recall_frame(self): - preset = self - - if preset.f is None: - return - - self.pc.data.settings['shader']['X3_AS_SPEED']['value'] = preset.f.get('x3_as_speed') - - # x3_as_speed affects preset recall, so do that first - self.recall_frame_params() - - for (layer, slot) in enumerate(preset.f.get('selected_shader_slots',[])): - if slot is not None: - #print("setting layer %s to slot %s" % (layer, slot)) - self.pc.actions.call_method_name('play_shader_%s_%s' % (layer, slot)) - - for (layer, active) in enumerate(preset.f.get('layer_active_status',[])): - # print ("got %s layer with status %s " % (layer,active)) - if active=='▶': - self.pc.actions.call_method_name('start_shader_layer_%s' % layer) - else: - self.pc.actions.call_method_name('stop_shader_layer_%s' % layer) - - def merge(self, frame2): - from copy import deepcopy - f = deepcopy(self.f) #frame1.copy() - #if self.DEBUG_FRAMES: print("merge_frames: got frame1\t%s" % frame1) - #if self.DEBUG_FRAMES: print("merge_frames: got frame2\t%s" % frame2) - for i,f2 in enumerate(frame2.f.get('shader_params')): - for i2,p in enumerate(f2): - if p is not None: - if 'shader_params' not in f: - f['shader_params'] = [[None]*4,[None]*4,[None]*4] - f['shader_params'][i][i2] = p - - if frame2.f.get('feedback_active') is not None: - f['feedback_active'] = frame2['feedback_active'] - - if frame2.f.get('x3_as_speed') is not None: - f['x3_as_speed'] = frame2.f.get('x3_as_speed') - - if f.get('shader_speeds') is None: - if 'shader_speeds' in frame2.f: - f['shader_speeds'] = frame2.f.get('shader_speeds') - else: - for i,s in enumerate(frame2.f.get('shader_speeds')): - if s is not None: - f['shader_speeds'][i] = s - - if frame2.f.get('strobe_amount'): - f['strobe_amount'] = frame2.f.get('strobe_amount') - - if self.DEBUG_FRAMES: print("merge_frames: got return\t%s" % f) - return Frame(self.pc).store_copy(f) - - def get_ignored(self, ignored): - from copy import deepcopy - f = deepcopy(self.f) #frame1.copy() - frame = self.f - ignored = ignored.f - if self.DEBUG_FRAMES: print("get_frame_ignored: got frame\t%s" % self.f) - for i,f2 in enumerate(frame.get('shader_params',[])): - for i2,p in enumerate(f2): - if ignored['shader_params'][i][i2] is not None: - f['shader_params'][i][i2] = None - if ignored.get('feedback_active') is not None: - f['feedback_active'] = None - if ignored.get('x3_as_speed') is not None: - f['x3_as_speed'] = None - if ignored.get('shader_speeds') is not None and frame.get('shader_speeds') is not None: - for i,s in enumerate(frame.get('shader_speeds')): - if ignored['shader_speeds'][i] is not None: - f['shader_speeds'][i] = None - if ignored.get('strobe_amount') is not None: - f['strobe_amount'] = None - if self.DEBUG_FRAMES: print("get_frame_ignored: got return\t%s" % f) - return Frame(self.pc).store_copy(f) - - def is_empty(self): - #from copy import deepcopy - #f = deepcopy(frame) #frame1.copy() - frame = self.f - if self.DEBUG_FRAMES: print("is_frame_empty: got frame\t%s" % frame) - - if frame.get('feedback_active') is not None: - return False - if frame.get('x3_as_speed') is not None: - return False - if frame.get('strobe_amount') is not None: - return False - - for i,f in enumerate(frame['shader_params']): - for i2,p in enumerate(f): - if p is not None: #ignored['shader_params'][i][i2] is not None: - return False - - if frame.get('shader_speeds') is not None: - for i,f in enumerate(frame['shader_speeds']): - if f is not None: - return False - - if self.DEBUG_FRAMES: print("is_frame_empty: got return true") - return True - - def get_diff(self, current_frame): - #if not last_frame: return current_frame - current_frame = current_frame.f - last_frame = self.f - - if self.DEBUG_FRAMES: - print(">>>>get_frame_diff>>>>") - print("last_frame: \t%s" % last_frame['shader_params']) - print("current_frame: \t%s" % current_frame['shader_params']) - - param_values = [[None]*4,[None]*4,[None]*4] - for layer,params in enumerate(current_frame.get('shader_params',[[None]*4]*3)): - #if self.DEBUG_FRAMES: print("got layer %s params: %s" % (layer, params)) - for param,p in enumerate(params): - if p is not None and p != last_frame.get('shader_params')[layer][param]: - if self.DEBUG_FRAMES: print("setting layer %s param %s to %s" % (layer,param,p)) - param_values[layer][param] = p - - if current_frame['feedback_active'] is not None and last_frame['feedback_active'] != current_frame['feedback_active']: - feedback_active = current_frame['feedback_active'] - else: - feedback_active = None - - if current_frame['x3_as_speed'] is not None and last_frame['x3_as_speed'] != current_frame['x3_as_speed']: - x3_as_speed = current_frame['x3_as_speed'] - else: - x3_as_speed = None - - speed_values = [None]*3 - for layer,param in enumerate(current_frame.get('shader_speeds',[None]*3)): - if param is not None and param != last_frame['shader_speeds'][layer]: - speed_values[layer] = param - - if current_frame['strobe_amount'] is not None and last_frame['strobe_amount'] != current_frame['strobe_amount']: - strobe_amount = current_frame['strobe_amount'] - else: - strobe_amount = None - - if self.DEBUG_FRAMES: - print("param_values is\t%s" % param_values) - print("speed_values is\t%s" % speed_values) - - diff = { - 'shader_params': param_values, - 'feedback_active': feedback_active, - 'x3_as_speed': x3_as_speed, - 'shader_speeds': speed_values, - 'strobe_amount': strobe_amount, - } - if self.DEBUG_FRAMES: print("returning\t%s\n^^^^" % diff['shader_params']) - - return Frame(self.pc).store_copy(diff) - - - - - -class FrameManager: - pc = None - - def __init__(self, pc): - self.pc = pc - - def get_live_frame(self): - return Frame(self.pc).store_live() - - def recall_frame_params(self, preset): - if preset is None: - return - preset.recall_frame_params() - - def recall_frame(self, preset): - if preset is None: - return - preset.recall_frame() - - # overlay frame2 on frame1 - def merge_frames(self, frame1, frame2): - return frame1.merge(frame2) - - def get_frame_ignored(self, frame, ignored): - return frame.get_ignored(ignored) - - def is_frame_empty(self, frame): - return frame.is_empty() - - def get_frame_diff(self, last_frame, current_frame): - return last_frame.get_diff(current_frame) - diff --git a/video_centre/shaders.py b/video_centre/shaders.py index a9bbf26..6d8e69d 100644 --- a/video_centre/shaders.py +++ b/video_centre/shaders.py @@ -20,6 +20,8 @@ class Shaders(object): self.selected_modulation_slot = 0 self.selected_status_list = ['-','-','-'] ## going to try using symbols for this : '-' means empty, '▶' means running, '■' means not running, '!' means error + self.selected_modulation_level = [[[0.0,0.0,0.0,0.0] for i in range(4)] for i in range(3)] + self.modulation_value = [0.0,0.0,0.0,0.0] self.selected_param_list = [[0.0,0.0,0.0,0.0] for i in range(3)] self.selected_speed_list = [1.0, 1.0, 1.0]