mirror of
https://github.com/cyberboy666/r_e_c_u_r.git
synced 2025-12-11 19:00:04 +01:00
Backported basic code for MIDI controller autodetection (custom config file per controller), plugins with example plugin, and Akai APC Key 25 feedback plugin
This commit is contained in:
16
ACTIONS.md
16
ACTIONS.md
@@ -1,8 +1,8 @@
|
||||
# Auto-generated Actions list
|
||||
|
||||
Fri 3 Jan 22:08:36 UTC 2020
|
||||
Fri 10 Jan 17:25:14 UTC 2020
|
||||
|
||||
for branch=feature_shader_midi
|
||||
for branch=feature_plugins
|
||||
|
||||
# Methods
|
||||
* change_composite_setting(setting_value)
|
||||
@@ -58,6 +58,7 @@ for branch=feature_shader_midi
|
||||
* load_this_detour_shader
|
||||
* _load_this_slot_into_next_player(slot)
|
||||
* map_on_shaders_selection
|
||||
* modulate_param_layer_offset_to_amount(param, layer, amount)
|
||||
* move_browser_selection_down
|
||||
* move_browser_selection_up
|
||||
* move_settings_selection_down
|
||||
@@ -156,7 +157,7 @@ for branch=feature_shader_midi
|
||||
* toggle_x_autorepeat
|
||||
* try_pull_code_and_reset
|
||||
|
||||
# Dynamic routes
|
||||
## Dynamic routes
|
||||
* play_shader_([0-9])_([0-9])
|
||||
* toggle_shader_layer_([0-2])
|
||||
* start_shader_layer_([0-2])
|
||||
@@ -171,6 +172,15 @@ for branch=feature_shader_midi
|
||||
* set_shader_speed_layer_offset_([0-2])_amount
|
||||
* set_shader_speed_layer_([0-2])_amount
|
||||
|
||||
### Plugin routes
|
||||
* test_plugin (from MidiActionsTestPlugin)
|
||||
* cycle_shaders (from MidiActionsTestPlugin)
|
||||
* run_automation (from MidiActionsTestPlugin)
|
||||
* stop_automation (from MidiActionsTestPlugin)
|
||||
* toggle_pause_automation (from MidiActionsTestPlugin)
|
||||
* pause_automation (from MidiActionsTestPlugin)
|
||||
* toggle_loop_automation (from MidiActionsTestPlugin)
|
||||
|
||||
----
|
||||
|
||||
Autogenerated by dotfiles/generate-list-actions.sh
|
||||
|
||||
28
actions.py
28
actions.py
@@ -530,7 +530,7 @@ class Actions(object):
|
||||
self.data.settings['shader']['STROBE_AMOUNT']['value'] = scaled_amount
|
||||
|
||||
def get_midi_status(self):
|
||||
self.message_handler.set_message('INFO', 'midi status is {}'.format(self.data.midi_status))
|
||||
self.message_handler.set_message('INFO', ("midi status is {} to %s"%(self.data.midi_device_name)).format(self.data.midi_status))
|
||||
|
||||
def cycle_midi_port_index(self):
|
||||
self.data.midi_port_index = self.data.midi_port_index + 1
|
||||
@@ -591,8 +591,9 @@ class Actions(object):
|
||||
|
||||
def check_if_should_start_openframeworks(self):
|
||||
if self.data.settings['video']['VIDEOPLAYER_BACKEND']['value'] != 'omxplayer':
|
||||
self.openframeworks_process = subprocess.Popen([self.data.PATH_TO_OPENFRAMEWORKS +'apps/myApps/c_o_n_j_u_r/bin/c_o_n_j_u_r'])
|
||||
print('conjur pid is {}'.format(self.openframeworks_process.pid))
|
||||
with open("conjur.log","w+") as out:
|
||||
self.openframeworks_process = subprocess.Popen([self.data.PATH_TO_OPENFRAMEWORKS +'apps/myApps/c_o_n_j_u_r/bin/c_o_n_j_u_r'], stdout=out)
|
||||
print('conjur pid is {}'.format(self.openframeworks_process.pid))
|
||||
|
||||
def exit_openframeworks(self):
|
||||
self.video_driver.osc_client.send_message("/exit", True)
|
||||
@@ -736,13 +737,13 @@ class Actions(object):
|
||||
options = self.data.settings['shader']['SHADER_PARAM']['options']
|
||||
current_index = [index for index, item in enumerate(options) if item == self.data.settings['shader']['SHADER_PARAM']['value'] ][0]
|
||||
self.data.settings['shader']['SHADER_PARAM']['value'] = options[(current_index + 1) % len(options) ]
|
||||
self.message_handler.set_message('INFO', 'The Param amountis now ' + str(self.data.settings['shader']['SHADER_PARAM']['value']))
|
||||
self.message_handler.set_message('INFO', 'The Param amount is now ' + str(self.data.settings['shader']['SHADER_PARAM']['value']))
|
||||
|
||||
def decrease_shader_param(self):
|
||||
options = self.data.settings['shader']['SHADER_PARAM']['options']
|
||||
current_index = [index for index, item in enumerate(options) if item == self.data.settings['shader']['SHADER_PARAM']['value'] ][0]
|
||||
self.data.settings['shader']['SHADER_PARAM']['value'] = options[(current_index - 1) % len(options) ]
|
||||
self.message_handler.set_message('INFO', 'The Param amountis now ' + str(self.data.settings['shader']['SHADER_PARAM']['value']))
|
||||
self.message_handler.set_message('INFO', 'The Param amount is now ' + str(self.data.settings['shader']['SHADER_PARAM']['value']))
|
||||
|
||||
|
||||
def set_fixed_length(self, value):
|
||||
@@ -893,6 +894,9 @@ class Actions(object):
|
||||
def clear_message(self):
|
||||
self.message_handler.clear_all_messages()
|
||||
|
||||
def modulate_param_layer_offset_to_amount(self, param, layer, amount):
|
||||
self.shaders.modulate_param_layer_offset_to_amount(param, amount, layer_offset=layer)
|
||||
|
||||
@staticmethod
|
||||
def try_remove_file(path):
|
||||
if os.path.exists(path):
|
||||
@@ -907,7 +911,7 @@ class Actions(object):
|
||||
( r"toggle_shader_layer_([0-2])", self.toggle_shader_layer ),
|
||||
( r"start_shader_layer_([0-2])", self.shaders.start_shader ),
|
||||
( r"stop_shader_layer_([0-2])", self.shaders.stop_shader ),
|
||||
( r"set_the_shader_param_([0-3])_layer_([0-2])_continuous", self.shaders.set_param_layer_to_amount ),
|
||||
( r"set_the_shader_param_([0-3])_layer_([0-2])_continuous", self.shaders.set_param_layer_to_amount ),
|
||||
( r"modulate_param_([0-3])_to_amount_continuous", self.shaders.modulate_param_to_amount ),
|
||||
( r"set_param_([0-3])_layer_([0-2])_modulation_level_continuous", self.shaders.set_param_layer_offset_modulation_level ),
|
||||
( r"set_param_([0-3])_layer_offset_([0-2])_modulation_level_continuous", self.shaders.set_param_layer_offset_modulation_level ),
|
||||
@@ -915,7 +919,7 @@ class Actions(object):
|
||||
( r"reset_modulation_([0-3])", self.shaders.reset_modulation ),
|
||||
( 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_shader_speed_layer_([0-2])_amount", self.shaders.set_speed_layer_to_amount ),
|
||||
}
|
||||
|
||||
def detect_types(self, args):
|
||||
@@ -951,6 +955,16 @@ class Actions(object):
|
||||
|
||||
|
||||
def call_parse_method_name(self, method_name, argument):
|
||||
# first test if a registered plugin handles this for us
|
||||
from data_centre.plugin_collection import ActionsPlugin
|
||||
for plugin in self.data.plugins.get_plugins(ActionsPlugin):
|
||||
if plugin.is_handled(method_name):
|
||||
print ("Plugin %s is handling %s" % (plugin, method_name))
|
||||
method, arguments = plugin.get_callback_for_method(method_name, argument)
|
||||
method(*arguments)
|
||||
return
|
||||
|
||||
# if not then fall back to using internal method
|
||||
try:
|
||||
method, arguments = self.get_callback_for_method(method_name, argument)
|
||||
method(*arguments)
|
||||
|
||||
@@ -7,6 +7,24 @@ import inspect
|
||||
from itertools import cycle
|
||||
from omxplayer.player import OMXPlayer
|
||||
from shutil import copyfile
|
||||
import threading
|
||||
|
||||
from data_centre import plugin_collection
|
||||
|
||||
class AsyncWrite(threading.Thread):
|
||||
def __init__(self, filename, data, mode='json'):
|
||||
threading.Thread.__init__(self)
|
||||
self.filename = filename
|
||||
self.data = data
|
||||
self.mode = mode
|
||||
|
||||
def run(self):
|
||||
with open(self.filename, "w+") as data_file:
|
||||
if self.mode=='json':
|
||||
json.dump(self.data, data_file, indent=4, sort_keys=True)
|
||||
else:
|
||||
data_file.write(self.data)
|
||||
data_file.close()
|
||||
|
||||
|
||||
|
||||
@@ -33,6 +51,11 @@ class Data(object):
|
||||
#self.EMPTY_BANK = [self.EMPTY_SLOT for i in range(10)]
|
||||
self.PATHS_TO_BROWSER = [self.PATH_TO_EXTERNAL_DEVICES, '/home/pi/Videos' ]
|
||||
self.PATHS_TO_SHADERS = [self.PATH_TO_EXTERNAL_DEVICES, '/home/pi/r_e_c_u_r/Shaders', '/home/pi/Shaders' ]
|
||||
self.PATHS_TO_PLUGIN_DATA = [ '/home/pi/r_e_c_u_r/json_objects/plugins', self.PATH_TO_EXTERNAL_DEVICES ]
|
||||
|
||||
#initialise plugin manager
|
||||
self.plugins = plugin_collection.PluginCollection("plugins", message_handler, self)
|
||||
self.plugins.apply_all_plugins_on_value(5)
|
||||
|
||||
### state data
|
||||
self.auto_repeat_on = True
|
||||
@@ -78,7 +101,17 @@ class Data(object):
|
||||
self.midi_mappings = self._read_json(self.MIDI_MAPPING_JSON)
|
||||
self.analog_mappings = self._read_json(self.ANALOG_MAPPING_JSON)
|
||||
|
||||
|
||||
def load_midi_mapping_for_device(self, device_name):
|
||||
# check if custom config file exists on disk for this device name
|
||||
custom_file = self.MIDI_MAPPING_JSON.replace(".json","_%s.json"%device_name)
|
||||
if os.path.isfile(self.PATH_TO_DATA_OBJECTS + custom_file):
|
||||
self.midi_mappings = self._read_json(custom_file)
|
||||
self.message_handler.set_message('INFO', "Loaded %s for %s" % (custom_file, device_name)) #the slot you pressed is empty')
|
||||
print ("loaded custom midi mapping for %s" % custom_file)
|
||||
else:
|
||||
print ("loading default midi mapping for %s" % (device_name))
|
||||
self.midi_mappings = self._read_json(self.MIDI_MAPPINGS_JSON)
|
||||
return self.midi_mappings
|
||||
|
||||
|
||||
@staticmethod
|
||||
@@ -100,6 +133,23 @@ class Data(object):
|
||||
with open('{}{}'.format(self.PATH_TO_DATA_OBJECTS, file_name), 'w') as data_file:
|
||||
json.dump(data, data_file, indent=4, sort_keys=True)
|
||||
|
||||
def _read_plugin_json(self, file_name):
|
||||
for path in self.PATHS_TO_PLUGIN_DATA:
|
||||
print("trying path %s"%path)
|
||||
try:
|
||||
with open("%s/%s" % (path,file_name)) as data_file:
|
||||
data = json.load(data_file)
|
||||
return data
|
||||
except:
|
||||
pass
|
||||
print ("no plugin data loaded for %s" % file_name)
|
||||
|
||||
def _update_plugin_json(self, file_name, data):
|
||||
#with open("%s/%s" % (self.PATHS_TO_PLUGIN_DATA[0], file_name), "w+") as data_file:
|
||||
# json.dump(data, data_file, indent=4, sort_keys=True)
|
||||
writer = AsyncWrite("%s/%s" % (self.PATHS_TO_PLUGIN_DATA[0], file_name), data, mode='json')
|
||||
writer.start()
|
||||
|
||||
def update_conjur_dev_mode(self, value):
|
||||
print(value)
|
||||
tree = ET.parse(self.PATH_TO_CONJUR_DATA)
|
||||
|
||||
270
data_centre/plugin_collection.py
Normal file
270
data_centre/plugin_collection.py
Normal file
@@ -0,0 +1,270 @@
|
||||
import inspect
|
||||
import os
|
||||
import pkgutil
|
||||
import re
|
||||
|
||||
|
||||
class Plugin(object):
|
||||
"""Base class that each plugin must inherit from. within this class
|
||||
you must define the methods that all of your plugins must implement
|
||||
"""
|
||||
disabled = False
|
||||
|
||||
def __init__(self, plugin_collection):
|
||||
self.description = 'UNKNOWN'
|
||||
self.pc = plugin_collection
|
||||
|
||||
class MidiFeedbackPlugin(Plugin):
|
||||
"""Base class for MIDI feedback plugins
|
||||
"""
|
||||
def __init__(self, plugin_collection):
|
||||
super().__init__(plugin_collection)
|
||||
self.description = 'Outputs feedback about status to device eg MIDI pads'
|
||||
|
||||
def supports_midi_feedback(self, device_name):
|
||||
return False
|
||||
|
||||
def set_midi_device(self, midi_device):
|
||||
self.midi_feedback_device = midi_device
|
||||
|
||||
def refresh_midi_feedback(self):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
|
||||
class SequencePlugin(Plugin):
|
||||
def __init__(self, plugin_collection):
|
||||
super().__init__(plugin_collection)
|
||||
|
||||
@property
|
||||
def parserlist(self):
|
||||
return [
|
||||
( r"run_automation", self.run_automation ),
|
||||
( r"stop_automation", self.stop_automation ),
|
||||
( r"toggle_pause_automation", self.toggle_pause_automation ),
|
||||
( r"pause_automation", self.pause_automation ),
|
||||
( r"toggle_loop_automation", self.toggle_loop_automation ),
|
||||
]
|
||||
|
||||
def position(self, now):
|
||||
import time
|
||||
passed = now - self.automation_start
|
||||
if self.duration>0:
|
||||
position = passed / self.duration*1000
|
||||
return position
|
||||
|
||||
def toggle_automation(self):
|
||||
if not self.is_playing():
|
||||
self.run_automation()
|
||||
else:
|
||||
self.stop_automation()
|
||||
|
||||
def toggle_loop_automation(self):
|
||||
self.looping = not self.looping
|
||||
|
||||
def pause_automation(self):
|
||||
self.pause_flag = not self.is_paused() and self.is_playing()
|
||||
|
||||
def stop_automation(self):
|
||||
self.stop_flag = True
|
||||
|
||||
def toggle_pause_automation(self):
|
||||
self.pause_flag = not self.is_paused()
|
||||
self.pause_flag = self.is_paused() and self.is_playing()
|
||||
if not self.is_paused() and not self.is_playing():
|
||||
self.run_automation()
|
||||
|
||||
store_passed = None
|
||||
pause_flag = True
|
||||
stop_flag = False
|
||||
looping = True
|
||||
automation_start = None
|
||||
iterations_count = 0
|
||||
duration = 2000
|
||||
frequency = 100
|
||||
def run_automation(self):
|
||||
import time
|
||||
|
||||
now = time.time()
|
||||
|
||||
if self.looping and self.automation_start is not None and (now - self.automation_start >= self.duration/1000):
|
||||
print("restarting as start reached %s" % self.automation_start)
|
||||
self.iterations_count += 1
|
||||
self.automation_start = None
|
||||
|
||||
if not self.automation_start:
|
||||
self.automation_start = now
|
||||
print ("%s: starting automation" % self.automation_start)
|
||||
self.pause_flag = False
|
||||
|
||||
#print("running automation at %s!" % self.position)
|
||||
if not self.is_paused():
|
||||
self.store_passed = None
|
||||
self.run_sequence(self.position(now))
|
||||
#print ("%s: automation_start is %s" % (time.time()-self.automation_start,self.automation_start))
|
||||
else:
|
||||
#print ("%s: about to reset automation_start" % self.automation_start)
|
||||
#print (" got passed %s" % (time.time() - self.automation_start))
|
||||
if not self.store_passed:
|
||||
self.store_passed = (now - self.automation_start)
|
||||
self.automation_start = now - self.store_passed
|
||||
#print ("%s: reset automation_start to %s" % (time.time()-self.automation_start,self.automation_start))
|
||||
#return
|
||||
|
||||
if (now - self.automation_start < self.duration/1000) and not self.stop_flag:
|
||||
self.pc.midi_input.root.after(self.frequency, self.run_automation)
|
||||
else:
|
||||
print("%s: stopping ! (stop_flag %s)" % ((now - self.automation_start),self.stop_flag) )
|
||||
self.stop_flag = False
|
||||
self.automation_start = None
|
||||
self.iterations_count = 0
|
||||
|
||||
def is_paused(self):
|
||||
return self.pause_flag
|
||||
|
||||
def is_playing(self):
|
||||
return self.automation_start is not None
|
||||
|
||||
def run_sequence(self, position):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class ActionsPlugin(Plugin):
|
||||
def __init__(self, plugin_collection):
|
||||
super().__init__(plugin_collection)
|
||||
|
||||
@property
|
||||
def parserlist(self):
|
||||
return [
|
||||
#( r"test_plugin", self.test_plugin )
|
||||
]
|
||||
|
||||
def is_handled(self, method_name):
|
||||
for a in self.parserlist:
|
||||
if (a[0]==method_name):
|
||||
return True
|
||||
regex = a[0]
|
||||
me = a[1]
|
||||
matches = re.match(regex, method_name)
|
||||
if matches:
|
||||
return True
|
||||
|
||||
|
||||
def get_callback_for_method(self, method_name, argument):
|
||||
for a in self.parserlist:
|
||||
regex = a[0]
|
||||
me = a[1]
|
||||
matches = re.search(regex, method_name)
|
||||
|
||||
if matches:
|
||||
found_method = me
|
||||
parsed_args = self.pc.actions.detect_types(matches.groups())
|
||||
if argument is not None:
|
||||
args = parsed_args + [argument]
|
||||
else:
|
||||
args = parsed_args
|
||||
|
||||
return (found_method, args)
|
||||
|
||||
#def call_parse_method_name(self, method_name, argument):
|
||||
# method, arguments = self.actions.get_callback_for_method(method_name, argument)
|
||||
# method(*arguments)
|
||||
|
||||
|
||||
# adapted from https://github.com/gdiepen/python_plugin_example
|
||||
class PluginCollection(object):
|
||||
"""Upon creation, this class will read the plugins package for modules
|
||||
that contain a class definition that is inheriting from the Plugin class
|
||||
"""
|
||||
|
||||
@property
|
||||
def shaders(self):
|
||||
return self.message_handler.shaders
|
||||
|
||||
@property
|
||||
def actions(self):
|
||||
return self.message_handler.actions
|
||||
|
||||
"""@property
|
||||
def midi_input(self):
|
||||
return self.data.midi_input"""
|
||||
|
||||
def __init__(self, plugin_package, message_handler, data):
|
||||
"""Constructor that initiates the reading of all available plugins
|
||||
when an instance of the PluginCollection object is created
|
||||
"""
|
||||
self.plugin_package = plugin_package
|
||||
self.message_handler = message_handler
|
||||
#self.shaders = lambda: data.shaders
|
||||
self.data = data
|
||||
#self.actions = message_handler.actions
|
||||
self.reload_plugins()
|
||||
|
||||
def read_json(self, file_name):
|
||||
return self.data._read_plugin_json(file_name)
|
||||
def update_json(self, file_name, data):
|
||||
return self.data._update_plugin_json(file_name, data)
|
||||
|
||||
def reload_plugins(self):
|
||||
"""Reset the list of all plugins and initiate the walk over the main
|
||||
provided plugin package to load all available plugins
|
||||
"""
|
||||
self.plugins = []
|
||||
self.seen_paths = []
|
||||
print()
|
||||
print("Looking for plugins under package %s" % self.plugin_package)
|
||||
self.walk_package(self.plugin_package)
|
||||
|
||||
|
||||
def get_plugins(self, clazz = None):
|
||||
if clazz:
|
||||
return [c for c in self.plugins if (isinstance(c, clazz) and not c.disabled)]
|
||||
else:
|
||||
return [c for c in self.plugins if not c.disabled]
|
||||
|
||||
def apply_all_plugins_on_value(self, argument):
|
||||
"""Apply all of the plugins on the argument supplied to this function
|
||||
"""
|
||||
print()
|
||||
print('Applying all plugins on value %s:' %argument)
|
||||
for plugin in self.plugins:
|
||||
#print(" Applying %s on value %s yields value %s" % (plugin.description, argument, plugin.perform_operation(argument)))
|
||||
pass
|
||||
|
||||
|
||||
def walk_package(self, package):
|
||||
"""Recursively walk the supplied package to retrieve all plugins
|
||||
"""
|
||||
imported_package = __import__(package, fromlist=['blah'])
|
||||
|
||||
for _, pluginname, ispkg in pkgutil.iter_modules(imported_package.__path__, imported_package.__name__ + '.'):
|
||||
if not ispkg:
|
||||
plugin_module = __import__(pluginname, fromlist=['blah'])
|
||||
clsmembers = inspect.getmembers(plugin_module, inspect.isclass)
|
||||
for (_, c) in clsmembers:
|
||||
# Only add classes that are a sub class of Plugin, but NOT Plugin itself
|
||||
# or one of the base classes
|
||||
ignore_list = [ Plugin, ActionsPlugin, SequencePlugin, MidiFeedbackPlugin ]
|
||||
if issubclass(c, Plugin) & (c not in ignore_list):
|
||||
print(' Found plugin class: %s.%s' % (c.__module__,c.__name__))
|
||||
self.plugins.append(c(self))
|
||||
|
||||
|
||||
# Now that we have looked at all the modules in the current package, start looking
|
||||
# recursively for additional modules in sub packages
|
||||
all_current_paths = []
|
||||
if isinstance(imported_package.__path__, str):
|
||||
all_current_paths.append(imported_package.__path__)
|
||||
else:
|
||||
all_current_paths.extend([x for x in imported_package.__path__])
|
||||
|
||||
for pkg_path in all_current_paths:
|
||||
if pkg_path not in self.seen_paths:
|
||||
self.seen_paths.append(pkg_path)
|
||||
|
||||
# Get all sub directory of the current package path directory
|
||||
child_pkgs = [p for p in os.listdir(pkg_path) if os.path.isdir(os.path.join(pkg_path, p))]
|
||||
|
||||
# For each sub directory, apply the walk_package method recursively
|
||||
for child_pkg in child_pkgs:
|
||||
self.walk_package(package + '.' + child_pkg)
|
||||
@@ -11,10 +11,14 @@ grep " def " actions.py | grep -v "^#" | sed -e 's/ def //' | sed -e 's/self//'
|
||||
| grep -v "parserlist\|check_if_should_start_openframeworks\|create_serial_port_process\|__init__\|persist_composite_setting\|receive_detour_info\|_refresh_frame_buffer\|refresh_frame_buffer_and_restart_openframeworks\|run_script\|setup_osc_server\|start_confirm_action\|stop_serial_port_process\|stop_openframeworks_process\|update_capture_settings\|update_config_settings\|update_video_settings\|try_remove_file\|get_callback\|call_method_name\|call_parse_method\|detect_types"
|
||||
echo
|
||||
|
||||
echo "# Dynamic routes"
|
||||
echo "## Dynamic routes"
|
||||
grep '( r"' actions.py | sed -e 's/\(.*\)"\(.*\)"\(.*\)/ * \2/'
|
||||
echo
|
||||
|
||||
echo "### Plugin routes"
|
||||
grep "( r\"" plugins/*.py | sed -e 's/plugins\/\(.*\)\.py:\(.*\)\( r\"\)\(.*\)\"\(.*\)/ * \4\t(from \1)/' | grep -v "open_serial"
|
||||
echo
|
||||
|
||||
echo "----"
|
||||
echo
|
||||
echo "Autogenerated by dotfiles/generate-list-actions.sh"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"control_change 0": {
|
||||
"DEFAULT": ["set_the_shader_param_0_layer_offset_0_continuous"],
|
||||
"DEFAULT": ["set_the_shader_param_0_layer_offset_0_continuous","set_strobe_amount_continuous"],
|
||||
"NAV_DETOUR": ["set_detour_mix_continuous"]
|
||||
},
|
||||
"control_change 1": {
|
||||
|
||||
266
json_objects/midi_action_mapping_APC Key 25.json
Normal file
266
json_objects/midi_action_mapping_APC Key 25.json
Normal file
@@ -0,0 +1,266 @@
|
||||
{
|
||||
"control_change 0": {
|
||||
"DEFAULT": ["set_the_shader_param_0_layer_offset_0_continuous"],
|
||||
"NAV_DETOUR": ["set_detour_mix_continuous"]
|
||||
},
|
||||
"control_change 1": {
|
||||
"DEFAULT": ["set_the_shader_param_1_layer_offset_0_continuous"],
|
||||
"NAV_DETOUR": ["set_detour_speed_position_continuous"]
|
||||
},
|
||||
"control_change 2": {
|
||||
"DEFAULT": ["set_the_shader_param_2_layer_offset_0_continuous"],
|
||||
"NAV_DETOUR": ["set_detour_start_continuous"]
|
||||
},
|
||||
"control_change 3": {
|
||||
"DEFAULT": ["set_the_shader_param_3_layer_offset_0_continuous"],
|
||||
"NAV_DETOUR": ["set_detour_end_continuous"]
|
||||
},
|
||||
"control_change 4": {
|
||||
"DEFAULT": ["set_the_shader_param_0_layer_offset_1_continuous"]
|
||||
},
|
||||
"control_change 5": {
|
||||
"DEFAULT": ["set_the_shader_param_1_layer_offset_1_continuous"]
|
||||
},
|
||||
"control_change 6": {
|
||||
"DEFAULT": ["set_the_shader_param_2_layer_offset_1_continuous"]
|
||||
},
|
||||
"control_change 7": {
|
||||
"DEFAULT": ["set_the_shader_param_3_layer_offset_1_continuous"]
|
||||
},
|
||||
"control_change 8": {
|
||||
"DEFAULT": ["set_the_shader_param_0_layer_offset_2_continuous"]
|
||||
},
|
||||
"control_change 9": {
|
||||
"DEFAULT": ["set_the_shader_param_1_layer_offset_2_continuous"]
|
||||
},
|
||||
"control_change 10": {
|
||||
"DEFAULT": ["set_the_shader_param_2_layer_offset_2_continuous"]
|
||||
},
|
||||
"control_change 11": {
|
||||
"DEFAULT": ["set_the_shader_param_3_layer_offset_2_continuous"]
|
||||
},
|
||||
"control_change 48": {
|
||||
"DEFAULT": ["set_the_shader_param_0_layer_offset_0_continuous","set_strobe_amount_continuous"],
|
||||
"NAV_DETOUR": ["set_detour_speed_position_continuous"]
|
||||
},
|
||||
"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"]
|
||||
},
|
||||
"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"]
|
||||
},
|
||||
"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"]
|
||||
},
|
||||
"control_change 52": {
|
||||
"DEFAULT": ["set_the_shader_param_0_layer_offset_1_continuous","set_param_0_layer_offset_0_modulation_level_continuous"],
|
||||
"NAV_DETOUR": ["set_detour_speed_position_continuous"]
|
||||
},
|
||||
"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"]
|
||||
},
|
||||
"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"]
|
||||
},
|
||||
"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"]
|
||||
},
|
||||
"control_change 56": {
|
||||
"DEFAULT": ["set_the_shader_param_0_layer_offset_2_continuous"],
|
||||
"NAV_DETOUR": ["set_detour_speed_position_continuous"]
|
||||
},
|
||||
"control_change 57": {
|
||||
"DEFAULT": ["set_the_shader_param_1_layer_offset_2_continuous"],
|
||||
"NAV_DETOUR": ["set_detour_start_continuous"]
|
||||
},
|
||||
"control_change 58": {
|
||||
"DEFAULT": ["set_the_shader_param_2_layer_offset_2_continuous"],
|
||||
"NAV_DETOUR": ["set_detour_end_continuous"]
|
||||
},
|
||||
"control_change 59": {
|
||||
"DEFAULT": ["set_the_shader_param_3_layer_offset_2_continuous"],
|
||||
"NAV_DETOUR": ["set_detour_end_continuous"]
|
||||
},
|
||||
"note_on 64": {
|
||||
"DEFAULT": ["clear_automation"]
|
||||
},
|
||||
"note_on 65": {
|
||||
"DEFAULT": ["toggle_pause_automation"]
|
||||
},
|
||||
"note_on 66": {
|
||||
"DEFAULT": ["toggle_record_automation"]
|
||||
},
|
||||
"note_on 67": {
|
||||
"DEFAULT": ["toggle_overdub_automation"]
|
||||
},
|
||||
"note_on 68": {
|
||||
"DEFAULT": ["store_next_preset"]
|
||||
},
|
||||
"note_on 69": {
|
||||
"DEFAULT": ["store_current_preset","clear_current_preset"]
|
||||
},
|
||||
"note_on 0": {
|
||||
"DEFAULT": ["switch_to_preset_0","select_preset_0"]
|
||||
},
|
||||
"note_on 1": {
|
||||
"DEFAULT": ["switch_to_preset_1","select_preset_1"]
|
||||
},
|
||||
"note_on 2": {
|
||||
"DEFAULT": ["switch_to_preset_2","select_preset_2"]
|
||||
},
|
||||
"note_on 3": {
|
||||
"DEFAULT": ["switch_to_preset_3","select_preset_3"]
|
||||
},
|
||||
"note_on 4": {
|
||||
"DEFAULT": ["switch_to_preset_4","select_preset_4"]
|
||||
},
|
||||
"note_on 5": {
|
||||
"DEFAULT": ["switch_to_preset_5","select_preset_5"]
|
||||
},
|
||||
"note_on 6": {
|
||||
"DEFAULT": ["switch_to_preset_6","select_preset_6"]
|
||||
},
|
||||
"note_on 7": {
|
||||
"DEFAULT": ["switch_to_preset_7","select_preset_7"]
|
||||
},
|
||||
"note_on 81": {
|
||||
"DEFAULT": ["","reset_selected_modulation"]
|
||||
},
|
||||
"note_on 82": {
|
||||
"DEFAULT": ["toggle_shader_layer_0","select_shader_modulation_slot_0"]
|
||||
},
|
||||
"note_on 83": {
|
||||
"DEFAULT": ["toggle_shader_layer_1","select_shader_modulation_slot_1"]
|
||||
},
|
||||
"note_on 84": {
|
||||
"DEFAULT": ["toggle_shader_layer_2","select_shader_modulation_slot_2"]
|
||||
},
|
||||
"note_on 85": {
|
||||
"DEFAULT": ["toggle_feedback","select_shader_modulation_slot_3"]
|
||||
},
|
||||
"note_on 86": {
|
||||
"DEFAULT": ["toggle_capture_preview"]
|
||||
},
|
||||
"note_on 70": {
|
||||
"DEFAULT": ["previous_shader_layer"]
|
||||
},
|
||||
"note_on 71": {
|
||||
"DEFAULT": ["next_shader_layer"]
|
||||
},
|
||||
"note_on 8": {
|
||||
"DEFAULT": ["toggle_automation_clip_0","select_automation_clip_0"]
|
||||
},
|
||||
"note_on 9": {
|
||||
"DEFAULT": ["toggle_automation_clip_1","select_automation_clip_1"]
|
||||
},
|
||||
"note_on 10": {
|
||||
"DEFAULT": ["toggle_automation_clip_2","select_automation_clip_2"]
|
||||
},
|
||||
"note_on 11": {
|
||||
"DEFAULT": ["toggle_automation_clip_3","select_automation_clip_3"]
|
||||
},
|
||||
"note_on 12": {
|
||||
"DEFAULT": ["toggle_automation_clip_4","select_automation_clip_4"]
|
||||
},
|
||||
"note_on 13": {
|
||||
"DEFAULT": ["toggle_automation_clip_5","select_automation_clip_5"]
|
||||
},
|
||||
"note_on 14": {
|
||||
"DEFAULT": ["toggle_automation_clip_6","select_automation_clip_6"]
|
||||
},
|
||||
"note_on 15": {
|
||||
"DEFAULT": ["toggle_automation_clip_7","select_automation_clip_7"]
|
||||
},
|
||||
|
||||
"note_on 32": {
|
||||
"DEFAULT": ["play_shader_0_0"]
|
||||
},
|
||||
"note_on 33": {
|
||||
"DEFAULT": ["play_shader_0_1"]
|
||||
},
|
||||
"note_on 34": {
|
||||
"DEFAULT": ["play_shader_0_2"]
|
||||
},
|
||||
"note_on 35": {
|
||||
"DEFAULT": ["play_shader_0_3"]
|
||||
},
|
||||
"note_on 36": {
|
||||
"DEFAULT": ["play_shader_0_4"]
|
||||
},
|
||||
"note_on 37": {
|
||||
"DEFAULT": ["play_shader_0_5"]
|
||||
},
|
||||
"note_on 38": {
|
||||
"DEFAULT": ["play_shader_0_6"]
|
||||
},
|
||||
"note_on 39": {
|
||||
"DEFAULT": ["play_shader_0_7"]
|
||||
},
|
||||
"note_on 24": {
|
||||
"DEFAULT": ["play_shader_1_0"]
|
||||
},
|
||||
"note_on 25": {
|
||||
"DEFAULT": ["play_shader_1_1"]
|
||||
},
|
||||
"note_on 26": {
|
||||
"DEFAULT": ["play_shader_1_2"]
|
||||
},
|
||||
"note_on 27": {
|
||||
"DEFAULT": ["play_shader_1_3"]
|
||||
},
|
||||
"note_on 28": {
|
||||
"DEFAULT": ["play_shader_1_4"]
|
||||
},
|
||||
"note_on 29": {
|
||||
"DEFAULT": ["play_shader_1_5"]
|
||||
},
|
||||
"note_on 30": {
|
||||
"DEFAULT": ["play_shader_1_6"]
|
||||
},
|
||||
"note_on 31": {
|
||||
"DEFAULT": ["play_shader_1_7"]
|
||||
},
|
||||
"note_on 16": {
|
||||
"DEFAULT": ["play_shader_2_0"]
|
||||
},
|
||||
"note_on 17": {
|
||||
"DEFAULT": ["play_shader_2_1"]
|
||||
},
|
||||
"note_on 18": {
|
||||
"DEFAULT": ["play_shader_2_2"]
|
||||
},
|
||||
"note_on 19": {
|
||||
"DEFAULT": ["play_shader_2_3"]
|
||||
},
|
||||
"note_on 20": {
|
||||
"DEFAULT": ["play_shader_2_4"]
|
||||
},
|
||||
"note_on 21": {
|
||||
"DEFAULT": ["play_shader_2_5"]
|
||||
},
|
||||
"note_on 22": {
|
||||
"DEFAULT": ["play_shader_2_6"]
|
||||
},
|
||||
"note_on 23": {
|
||||
"DEFAULT": ["play_shader_2_7"]
|
||||
},
|
||||
"note_on 98": {
|
||||
"DEFAULT": ["function_on"]
|
||||
},
|
||||
"note_off 98": {
|
||||
"DEFAULT": ["function_off"]
|
||||
},
|
||||
"note_on 91": {
|
||||
"DEFAULT": ["send_serial_macro_0","send_serial_macro_1"]
|
||||
},
|
||||
"note_on 93": {
|
||||
"DEFAULT": ["send_serial_string_hellO","send_random_settings"]
|
||||
}
|
||||
}
|
||||
|
||||
51
plugins/MidiActionsTestPlugin.py
Normal file
51
plugins/MidiActionsTestPlugin.py
Normal file
@@ -0,0 +1,51 @@
|
||||
import data_centre.plugin_collection
|
||||
from data_centre.plugin_collection import ActionsPlugin, SequencePlugin
|
||||
|
||||
class MidiActionsTestPlugin(ActionsPlugin,SequencePlugin):
|
||||
disabled = True
|
||||
|
||||
def __init__(self, plugin_collection):
|
||||
super().__init__(plugin_collection)
|
||||
|
||||
@property
|
||||
def parserlist(self):
|
||||
return [
|
||||
( r"test_plugin", self.test_plugin ),
|
||||
( r"cycle_shaders", self.cycle_shaders ),
|
||||
( r"run_automation", self.run_automation ),
|
||||
( r"stop_automation", self.stop_automation ),
|
||||
( r"toggle_pause_automation", self.toggle_pause_automation ),
|
||||
( r"pause_automation", self.pause_automation ),
|
||||
( r"toggle_loop_automation", self.toggle_loop_automation ),
|
||||
]
|
||||
|
||||
def test_plugin(self):
|
||||
print ("TEST PLUGIN test_plugin CALLED!!")
|
||||
# can now access various parts of recur via self.pc
|
||||
|
||||
cycle_count = 0
|
||||
def cycle_shaders(self):
|
||||
print ("Cycle shaders!!!")
|
||||
if self.cycle_count>9:
|
||||
self.cycle_count = 0
|
||||
|
||||
for i,shader in enumerate(self.pc.message_handler.shaders.selected_shader_list):
|
||||
self.pc.midi_input.call_method_name(
|
||||
"play_shader_%s_%s" % (i, self.cycle_count), None
|
||||
)
|
||||
self.pc.midi_input.call_method_name(
|
||||
"start_shader_layer_%s" % i, None
|
||||
)
|
||||
self.cycle_count += 1
|
||||
|
||||
duration = 5000
|
||||
frequency = 50
|
||||
def run_sequence(self, position):
|
||||
self.pc.midi_input.call_method_name(
|
||||
"set_the_shader_param_0_layer_0_continuous", position
|
||||
)
|
||||
|
||||
self.pc.midi_input.call_method_name(
|
||||
"set_the_shader_param_1_layer_0_continuous", position
|
||||
)
|
||||
|
||||
202
plugins/MidiFeedbackAPCKey25Plugin.py
Normal file
202
plugins/MidiFeedbackAPCKey25Plugin.py
Normal file
@@ -0,0 +1,202 @@
|
||||
from data_centre import plugin_collection
|
||||
from data_centre.plugin_collection import MidiFeedbackPlugin
|
||||
import mido
|
||||
|
||||
class MidiFeedbackAPCKey25Plugin(MidiFeedbackPlugin):
|
||||
disabled = False
|
||||
|
||||
status = {}
|
||||
|
||||
def __init__(self, plugin_collection):
|
||||
super().__init__(plugin_collection)
|
||||
self.description = 'Outputs feedback to APC Key 25'
|
||||
|
||||
def set_midi_device(self, device):
|
||||
super().set_midi_device(device)
|
||||
self.last_state = None
|
||||
|
||||
def supports_midi_feedback(self, device_name):
|
||||
supported_devices = ['APC Key 25']
|
||||
for supported_device in supported_devices:
|
||||
if device_name.startswith(supported_device):
|
||||
return True
|
||||
|
||||
def set_status(self, command='note_on', note=None, velocity=None):
|
||||
self.status[note] = {
|
||||
'command': command,
|
||||
'note': note,
|
||||
'velocity': velocity
|
||||
}
|
||||
#print("set status to %s: %s" % (note, self.status[note]))
|
||||
|
||||
def send_command(self, command='note_on', note=None, velocity=None):
|
||||
#print("send_command(%s, %s)" % (note, velocity))
|
||||
self.midi_feedback_device.send(
|
||||
mido.Message(command, note=note, velocity=velocity)
|
||||
)
|
||||
|
||||
def feedback_shader_feedback(self, on):
|
||||
self.set_status(note=85, velocity=int(on))
|
||||
|
||||
def feedback_capture_preview(self, on):
|
||||
self.set_status(note=86, velocity=int(on))
|
||||
|
||||
def feedback_shader_on(self, layer, slot, colour=None):
|
||||
if colour is None: colour = self.COLOUR_GREEN
|
||||
self.set_status(note=(32-(layer)*8)+slot, velocity=int(colour))
|
||||
|
||||
def feedback_shader_off(self, layer, slot):
|
||||
self.set_status(note=(32-(layer)*8)+slot, velocity=self.COLOUR_OFF)
|
||||
|
||||
NOTE_SCENE_LAUNCH_COLUMN = 82
|
||||
def feedback_shader_layer_on(self, layer):
|
||||
self.set_status(note=self.NOTE_SCENE_LAUNCH_COLUMN+layer, velocity=self.COLOUR_GREEN)
|
||||
|
||||
def feedback_shader_layer_off(self, layer):
|
||||
self.set_status(note=self.NOTE_SCENE_LAUNCH_COLUMN+layer, velocity=self.COLOUR_OFF)
|
||||
|
||||
def feedback_show_layer(self, layer):
|
||||
self.set_status(note=70, velocity=layer)
|
||||
|
||||
def feedback_show_modulation(self, slot):
|
||||
for i in range(self.NOTE_SCENE_LAUNCH_COLUMN,self.NOTE_SCENE_LAUNCH_COLUMN+4):
|
||||
if slot==i-self.NOTE_SCENE_LAUNCH_COLUMN:
|
||||
self.set_status(note=i, velocity=self.COLOUR_GREEN)
|
||||
else:
|
||||
self.set_status(note=i, velocity=self.COLOUR_OFF)
|
||||
|
||||
def feedback_plugin_status(self):
|
||||
from data_centre.plugin_collection import SequencePlugin
|
||||
|
||||
from plugins.MidiActionsTestPlugin import MidiActionsTestPlugin
|
||||
from plugins.ShaderLoopRecordPlugin import ShaderLoopRecordPlugin
|
||||
for plugin in self.pc.get_plugins(SequencePlugin):
|
||||
if isinstance(plugin, ShaderLoopRecordPlugin): #MidiActionsTestPlugin):
|
||||
|
||||
NOTE_PLAY_STATUS = 65
|
||||
NOTE_RECORD_STATUS = 66
|
||||
NOTE_OVERDUB_STATUS = 67
|
||||
NOTE_CLIP_STATUS_ROW = 8
|
||||
|
||||
colour = self.COLOUR_OFF
|
||||
if plugin.is_playing():
|
||||
colour = self.COLOUR_GREEN
|
||||
if plugin.is_paused():
|
||||
colour += self.BLINK
|
||||
self.set_status(command='note_on', note=NOTE_PLAY_STATUS, velocity=colour)
|
||||
|
||||
colour = self.COLOUR_OFF
|
||||
if plugin.recording:
|
||||
colour = self.COLOUR_GREEN
|
||||
if plugin.is_ignoring():
|
||||
colour += self.BLINK
|
||||
self.set_status(command='note_on', note=NOTE_RECORD_STATUS, velocity=colour)
|
||||
|
||||
colour = self.COLOUR_OFF
|
||||
if plugin.overdub:
|
||||
colour = self.COLOUR_RED
|
||||
if plugin.is_paused() or plugin.is_ignoring():
|
||||
colour += self.BLINK
|
||||
self.set_status(command='note_on', note=NOTE_OVERDUB_STATUS, velocity=colour)
|
||||
|
||||
for i in range(plugin.MAX_CLIPS):
|
||||
if i in plugin.running_clips:
|
||||
if plugin.is_playing() and not plugin.is_paused():
|
||||
colour = self.COLOUR_GREEN
|
||||
else:
|
||||
colour = self.COLOUR_AMBER
|
||||
if plugin.selected_clip==i: #blink if selected
|
||||
colour += self.BLINK
|
||||
elif plugin.selected_clip==i:
|
||||
colour = self.COLOUR_RED_BLINK
|
||||
else:
|
||||
colour = self.COLOUR_OFF
|
||||
self.set_status(command='note_on', note=NOTE_CLIP_STATUS_ROW+i, velocity=colour)
|
||||
|
||||
|
||||
from plugins.ShaderQuickPresetPlugin import ShaderQuickPresetPlugin
|
||||
#print ("feedback_plugin_status")
|
||||
for plugin in self.pc.get_plugins(ShaderQuickPresetPlugin):
|
||||
#print ("for plugin %s" % plugin)
|
||||
for pad in range(0,8):
|
||||
#print ("checking selected_preset %s vs pad %s" % (plugin.selected_preset, pad))
|
||||
colour = self.COLOUR_OFF
|
||||
if plugin.presets[pad] is not None:
|
||||
colour = self.COLOUR_AMBER
|
||||
if plugin.last_recalled==pad:
|
||||
colour = self.COLOUR_GREEN
|
||||
if plugin.selected_preset==pad:
|
||||
if plugin.presets[pad] is None:
|
||||
colour = self.COLOUR_RED
|
||||
colour += self.BLINK
|
||||
self.set_status(command='note_on', note=pad, velocity=colour)
|
||||
|
||||
BLINK = 1
|
||||
COLOUR_OFF = 0
|
||||
COLOUR_GREEN = 1
|
||||
COLOUR_GREEN_BLINK = 2
|
||||
COLOUR_RED = 3
|
||||
COLOUR_RED_BLINK = 4
|
||||
COLOUR_AMBER = 5
|
||||
COLOUR_AMBER_BLINK = 6
|
||||
|
||||
def refresh_midi_feedback(self):
|
||||
|
||||
# show which layer is selected (where parameter offset goes to)
|
||||
self.feedback_show_layer(self.pc.data.shader_layer)
|
||||
|
||||
# show if internal feedback (the shader layer kind) is enabled
|
||||
if self.pc.data.feedback_active and not self.pc.data.function_on:
|
||||
self.feedback_shader_feedback(self.COLOUR_GREEN)
|
||||
#elif self.pc.data.settings['shader']['X3_AS_SPEED']['value'] == 'enabled' and self.pc.data.function_on:
|
||||
# self.feedback_shader_feedback(self.COLOUR_GREEN_BLINK)
|
||||
else:
|
||||
self.feedback_shader_feedback(self.COLOUR_OFF)
|
||||
|
||||
if self.pc.message_handler.actions.display.capture.is_previewing and not self.pc.data.function_on:
|
||||
self.feedback_capture_preview(self.COLOUR_GREEN)
|
||||
else:
|
||||
self.feedback_capture_preview(self.COLOUR_OFF)
|
||||
|
||||
if self.pc.data.function_on:
|
||||
self.feedback_show_modulation(self.pc.shaders.selected_modulation_slot)
|
||||
|
||||
self.feedback_plugin_status()
|
||||
|
||||
for n,shader in enumerate(self.pc.message_handler.shaders.selected_shader_list):
|
||||
#print ("%s: in refresh_midi_feedback, got shader: %s" % (n,shader))
|
||||
# show if layer is running or not
|
||||
if not self.pc.data.function_on:
|
||||
if self.pc.message_handler.shaders.selected_status_list[n] == '▶':
|
||||
self.feedback_shader_layer_on(n)
|
||||
else:
|
||||
self.feedback_shader_layer_off(n)
|
||||
for x in range(0,8):
|
||||
if 'slot' in shader and shader.get('slot',None)==x:
|
||||
if self.pc.message_handler.shaders.selected_status_list[n] == '▶':
|
||||
# show that slot is selected and running
|
||||
self.feedback_shader_on(n, x, self.COLOUR_GREEN)
|
||||
else:
|
||||
# show that slot is selected but not running
|
||||
self.feedback_shader_on(n, x, self.COLOUR_AMBER_BLINK)
|
||||
elif self.pc.data.shader_bank_data[n][x]['path']:
|
||||
# show that slot is full but not selected
|
||||
self.feedback_shader_on(n, x, self.COLOUR_AMBER)
|
||||
else:
|
||||
# hos that nothing in slot
|
||||
self.feedback_shader_off(n, x)
|
||||
|
||||
self.update_device()
|
||||
|
||||
#print("refresh_midi_feedback")
|
||||
|
||||
last_state = None
|
||||
def update_device(self):
|
||||
from copy import deepcopy
|
||||
#print("in update device status is %s" % self.status)
|
||||
for i,c in self.status.items():
|
||||
#'print("comparing\n%s to\n%s" % (c, self.last_state[i]))
|
||||
if self.last_state is None or self.last_state[i]!=c:
|
||||
#print("got command: %s: %s" % (i,c))
|
||||
self.send_command(**c)
|
||||
self.last_state = deepcopy(self.status)
|
||||
@@ -52,10 +52,13 @@ display = Display(tk, video_driver, shaders, message_handler, data)
|
||||
|
||||
# setup the actions
|
||||
actions = Actions(tk, message_handler, data, video_driver, shaders, display, osc_client)
|
||||
message_handler.actions = actions
|
||||
|
||||
numpad_input = NumpadInput(tk, message_handler, display, actions, data)
|
||||
osc_input = OscInput(tk, message_handler, display, actions, data)
|
||||
midi_input = MidiInput(tk, message_handler, display, actions, data)
|
||||
data.plugins.midi_input = midi_input
|
||||
|
||||
analog_input = AnalogInput(tk, message_handler, display, actions, data)
|
||||
|
||||
actions.check_and_set_output_mode_on_boot()
|
||||
|
||||
@@ -9,13 +9,18 @@ class Shaders(object):
|
||||
self.root = root
|
||||
self.osc_client = osc_client
|
||||
self.message_handler = message_handler
|
||||
self.message_handler.shaders = self
|
||||
self.data = data
|
||||
self.shaders_menu = menu.ShadersMenu(self.data, self.message_handler, self.MENU_HEIGHT )
|
||||
self.selected_shader_list = [self.EMPTY_SHADER for i in range(3)]
|
||||
self.focused_param = 0
|
||||
self.shaders_menu_list = self.generate_shaders_list()
|
||||
|
||||
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]
|
||||
|
||||
@@ -87,11 +92,13 @@ class Shaders(object):
|
||||
|
||||
def start_shader(self, layer):
|
||||
self.osc_client.send_message("/shader/{}/is_active".format(str(layer)), True)
|
||||
self.selected_status_list[layer] = '▶'
|
||||
if self.selected_status_list[layer] != '-':
|
||||
self.selected_status_list[layer] = '▶'
|
||||
|
||||
def stop_shader(self, layer):
|
||||
self.osc_client.send_message("/shader/{}/is_active".format(str(layer)), False)
|
||||
self.selected_status_list[layer] = '■'
|
||||
if self.selected_status_list[layer] != '-':
|
||||
self.selected_status_list[layer] = '■'
|
||||
|
||||
def start_selected_shader(self):
|
||||
self.start_shader(self.data.shader_layer)
|
||||
@@ -128,9 +135,10 @@ class Shaders(object):
|
||||
|
||||
def play_that_shader(self, layer, slot):
|
||||
if self.data.shader_bank_data[layer][slot]['path']:
|
||||
self.selected_shader_list[layer] = self.data.shader_bank_data[layer][slot]
|
||||
self.selected_shader_list[layer]['slot'] = slot
|
||||
self.load_shader_layer(layer)
|
||||
if self.selected_shader_list[layer].get('slot') is None or self.selected_shader_list[layer]['slot'] != slot:
|
||||
self.selected_shader_list[layer] = self.data.shader_bank_data[layer][slot]
|
||||
self.selected_shader_list[layer]['slot'] = slot
|
||||
self.load_shader_layer(layer)
|
||||
else:
|
||||
self.message_handler.set_message('INFO', "shader slot %s:%s is empty"%(layer,slot))
|
||||
|
||||
@@ -259,4 +267,203 @@ class Shaders(object):
|
||||
self.osc_client.send_message("/shader/{}/speed".format(str(layer)), amount )
|
||||
self.selected_speed_list[layer] = amount
|
||||
|
||||
# methods for helping dealing with storing and recalling shader parameter frame states
|
||||
def get_live_frame(self):
|
||||
#print("get_live_frame: %s" % self.pc.message_handler.shaders.selected_param_list)
|
||||
import copy #from copy import deepcopy
|
||||
frame = {
|
||||
'selected_shader_slots': [ shader.get('slot',None) for shader in self.selected_shader_list ],
|
||||
'shader_params': copy.deepcopy(self.selected_param_list),
|
||||
'layer_active_status': copy.deepcopy(self.selected_status_list),
|
||||
'feedback_active': self.data.feedback_active,
|
||||
'x3_as_speed': self.data.settings['shader']['X3_AS_SPEED']['value'],
|
||||
'shader_speeds': copy.deepcopy(self.selected_speed_list),
|
||||
'strobe_amount': self.data.settings['shader']['STROBE_AMOUNT']['value'] / 10.0
|
||||
}
|
||||
#print("built frame: %s" % frame['shader_params'])
|
||||
return frame
|
||||
|
||||
def recall_frame_params(self, preset):
|
||||
if preset is None:
|
||||
return
|
||||
#print("recall_frame_params got: %s" % preset.get('shader_params'))
|
||||
for (layer, param_list) in enumerate(preset.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.data.plugins.actions.call_method_name('set_the_shader_param_%s_layer_%s_continuous' % (param,layer), value)
|
||||
|
||||
if preset.get('feedback_active') is not None:
|
||||
self.data.feedback_active = preset.get('feedback_active',self.data.feedback_active)
|
||||
if self.data.feedback_active:
|
||||
self.data.plugins.actions.call_method_name('enable_feedback')
|
||||
else:
|
||||
self.data.plugins.actions.call_method_name('disable_feedback')
|
||||
|
||||
if preset.get('x3_as_speed') is not None:
|
||||
self.data.settings['shader']['X3_AS_SPEED']['value'] = preset.get('x3_as_speed',self.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(preset.get('shader_speeds',[])):
|
||||
if speed is not None:
|
||||
self.data.plugins.actions.call_method_name('set_shader_speed_layer_%s_amount' % layer, speed)
|
||||
|
||||
if preset.get('strobe_amount') is not None:
|
||||
self.data.plugins.actions.set_strobe_amount_continuous(preset.get('strobe_amount'))
|
||||
|
||||
def recall_frame(self, preset):
|
||||
|
||||
self.data.settings['shader']['X3_AS_SPEED']['value'] = preset.get('x3_as_speed')
|
||||
|
||||
# x3_as_speed affects preset recall, so do that first
|
||||
self.recall_frame_params(preset)
|
||||
|
||||
for (layer, slot) in enumerate(preset.get('selected_shader_slots',[])):
|
||||
if slot is not None:
|
||||
#print("setting layer %s to slot %s" % (layer, slot))
|
||||
self.data.plugins.actions.call_method_name('play_shader_%s_%s' % (layer, slot))
|
||||
|
||||
for (layer, active) in enumerate(preset.get('layer_active_status',[])):
|
||||
# print ("got %s layer with status %s " % (layer,active))
|
||||
if active=='▶':
|
||||
self.data.plugins.actions.call_method_name('start_shader_layer_%s' % layer)
|
||||
else:
|
||||
self.data.plugins.actions.call_method_name('stop_shader_layer_%s' % layer)
|
||||
|
||||
DEBUG_FRAMES = False
|
||||
|
||||
# overlay frame2 on frame1
|
||||
def merge_frames(self, frame1, frame2):
|
||||
from copy import deepcopy
|
||||
f = deepcopy(frame1) #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['shader_params']):
|
||||
for i2,p in enumerate(f2):
|
||||
if p is not None:
|
||||
f['shader_params'][i][i2] = p
|
||||
|
||||
if frame2['feedback_active'] is not None:
|
||||
f['feedback_active'] = frame2['feedback_active']
|
||||
|
||||
if frame2['x3_as_speed'] is not None:
|
||||
f['x3_as_speed'] = frame2['x3_as_speed']
|
||||
|
||||
if f.get('shader_speeds') is None:
|
||||
f['shader_speeds'] = frame2.get('shader_speeds')
|
||||
else:
|
||||
for i,s in enumerate(frame2['shader_speeds']):
|
||||
if s is not None:
|
||||
f['shader_speeds'][i] = s
|
||||
|
||||
if frame2.get('strobe_amount'):
|
||||
f['strobe_amount'] = frame2.get('strobe_amount')
|
||||
|
||||
if self.DEBUG_FRAMES: print("merge_frames: got return\t%s" % f)
|
||||
return f
|
||||
|
||||
def get_frame_ignored(self, frame, ignored):
|
||||
from copy import deepcopy
|
||||
f = deepcopy(frame) #frame1.copy()
|
||||
if self.DEBUG_FRAMES: print("get_frame_ignored: got frame\t%s" % frame)
|
||||
for i,f2 in enumerate(frame['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'):
|
||||
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 f
|
||||
|
||||
def is_frame_empty(self, frame):
|
||||
#from copy import deepcopy
|
||||
#f = deepcopy(frame) #frame1.copy()
|
||||
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_frame_diff(self, last_frame, current_frame):
|
||||
if not last_frame: return current_frame
|
||||
|
||||
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 diff
|
||||
|
||||
|
||||
Reference in New Issue
Block a user