Merge pull request #137 from Cabalist/dev_cleanup

Code cleanup
This commit is contained in:
langolierz
2020-05-05 19:04:46 +02:00
committed by GitHub
32 changed files with 1658 additions and 1684 deletions

View File

@@ -1,20 +1,20 @@
import subprocess
import tracemalloc
import data_centre.length_setter as length_setter
from inspect import signature
import sys
import shlex
import argparse
import os
import re
from pythonosc import osc_message_builder
import subprocess
import sys
import threading
from inspect import signature
import git
from pythonosc import dispatcher
from pythonosc import osc_server
import git
import threading
import argparse
import data_centre.length_setter as length_setter
from video_centre.capture import Capture
from video_centre.of_capture import OfCapture
class Actions(object):
def __init__(self, tk, message_handler, data, video_driver, shaders, display, osc_client):
self.tk = tk
@@ -32,7 +32,7 @@ class Actions(object):
self.remote_process = None
self.set_capture_object('value')
self.server = self.setup_osc_server()
def set_capture_object(self, value):
if self.data.settings['video']['VIDEOPLAYER_BACKEND']['value'] != 'omxplayer':
self.python_capture.close_capture()
@@ -54,7 +54,6 @@ class Actions(object):
def move_browser_selection_page_up(self):
self.display.browser_menu.navigate_menu_page_up()
def enter_on_browser_selection(self):
self.display.browser_menu.enter_on_browser_selection()
@@ -70,7 +69,6 @@ class Actions(object):
def move_settings_selection_page_up(self):
self.display.settings_menu.navigate_menu_page_up()
def enter_on_settings_selection(self):
is_setting, setting = self.display.settings_menu.enter_on_setting_selection()
if is_setting:
@@ -92,7 +90,6 @@ class Actions(object):
def move_shaders_selection_page_up(self):
self.shaders.shaders_menu.navigate_menu_page_up()
def enter_on_shaders_selection(self):
##want to select shader if its not selected, and want to enter 'param' mode if it already is
is_shader, is_selected_shader, selected_shader = self.shaders.enter_on_shaders_selection()
@@ -122,7 +119,7 @@ class Actions(object):
self.display.browser_menu.generate_browser_list()
def _load_this_slot_into_next_player(self, slot):
### load next player for seamless type otherwise respect player mode
### load next player for seamless type otherwise respect player mode
if self.data.settings['sampler']['LOOP_TYPE']['value'] == 'seamless':
if self.data.update_next_slot_number(slot):
print('should reload next player !! ')
@@ -134,8 +131,6 @@ class Actions(object):
else:
if self.data.update_next_slot_number(slot, is_current=True):
self.video_driver.reload_current_player()
def load_slot_0_into_next_player(self):
self._load_this_slot_into_next_player(0)
@@ -177,8 +172,8 @@ class Actions(object):
def set_display_mode(self, display_mode):
mapmodes = {
'shader': 'SHADERS',
'shader_bank': 'SHDR_BNK'
'shader': 'SHADERS',
'shader_bank': 'SHDR_BNK'
}
if display_mode in mapmodes:
display_mode = mapmodes[display_mode]
@@ -186,7 +181,7 @@ class Actions(object):
display_mode = display_mode.upper()
display_modes = self.data.get_display_modes_list(with_nav_mode=True)
for i,dm in enumerate(display_modes):
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]
@@ -195,7 +190,7 @@ class Actions(object):
display_modes = self.data.get_display_modes_list(with_nav_mode=True)
current_mode_index = [index for index, i in enumerate(display_modes) if self.data.display_mode in i][0]
next_mode_index = (current_mode_index + 1) % len(display_modes)
next_mode_index = (current_mode_index + 1) % len(display_modes)
self.data.display_mode = display_modes[next_mode_index][0]
self.data.control_mode = display_modes[next_mode_index][1]
@@ -203,11 +198,10 @@ class Actions(object):
display_modes = self.data.get_display_modes_list(with_nav_mode=True)
current_mode_index = [index for index, i in enumerate(display_modes) if self.data.display_mode in i][0]
next_mode_index = (current_mode_index - 1) % len(display_modes)
next_mode_index = (current_mode_index - 1) % len(display_modes)
self.data.display_mode = display_modes[next_mode_index][0]
self.data.control_mode = display_modes[next_mode_index][1]
def toggle_action_on_player(self):
play = 'play' in self.data.settings['sampler']['ON_ACTION']['value']
show = 'show' in self.data.settings['sampler']['ON_ACTION']['value']
@@ -230,19 +224,17 @@ class Actions(object):
def increase_seek_time(self):
options = self.data.settings['sampler']['SEEK_TIME']['options']
current_index = [index for index, item in enumerate(options) if item == self.data.settings['sampler']['SEEK_TIME']['value'] ][0]
self.data.update_setting_value('sampler', 'SEEK_TIME', options[(current_index + 1) % len(options) ])
current_index = [index for index, item in enumerate(options) if item == self.data.settings['sampler']['SEEK_TIME']['value']][0]
self.data.update_setting_value('sampler', 'SEEK_TIME', options[(current_index + 1) % len(options)])
self.message_handler.set_message('INFO', 'The Seek Time is now ' + str(self.data.settings['sampler']['SEEK_TIME']['value']) + 's')
def decrease_seek_time(self):
options = self.data.settings['sampler']['SEEK_TIME']['options']
current_index = [index for index, item in enumerate(options) if item == self.data.settings['sampler']['SEEK_TIME']['value'] ][0]
self.data.update_setting_value('sampler', 'SEEK_TIME', options[(current_index - 1) % len(options) ])
current_index = [index for index, item in enumerate(options) if item == self.data.settings['sampler']['SEEK_TIME']['value']][0]
self.data.update_setting_value('sampler', 'SEEK_TIME', options[(current_index - 1) % len(options)])
self.message_handler.set_message('INFO', 'The Seek Time is now ' + str(self.data.settings['sampler']['SEEK_TIME']['value']) + 's')
def seek_forward_on_player(self):
def seek_forward_on_player(self):
self.video_driver.current_player.seek(self.data.settings['sampler']['SEEK_TIME']['value'])
def seek_back_on_player(self):
@@ -264,24 +256,24 @@ class Actions(object):
def previous_bank(self):
self.data.update_bank_number_by_amount(-1)
print('current bank is {} , the number of banks is {} '.format(self.data.bank_number, len(self.data.bank_data)))
def increase_speed(self):
print("increasing speed !")
new_rate = self.video_driver.current_player.change_rate(1)
current_bank, current_slot = self.data.split_bankslot_number(self.video_driver.current_player.bankslot_number)
self.data.update_slot_rate_to_this(current_slot, new_rate)
#self._load_this_slot_into_next_player(current_slot)
# self._load_this_slot_into_next_player(current_slot)
def decrease_speed(self):
print("increasing speed !")
new_rate = self.video_driver.current_player.change_rate(-1)
current_bank, current_slot = self.data.split_bankslot_number(self.video_driver.current_player.bankslot_number)
self.data.update_slot_rate_to_this(current_slot, new_rate)
#self._load_this_slot_into_next_player(current_slot)
# self._load_this_slot_into_next_player(current_slot)
def set_playing_sample_start_to_current_duration(self):
current_bank, current_slot = self.data.split_bankslot_number(self.video_driver.current_player.bankslot_number)
current_position = round(self.video_driver.current_player.get_position(),3)
current_position = round(self.video_driver.current_player.get_position(), 3)
self.data.update_slot_start_to_this_time(current_slot, current_position)
self._load_this_slot_into_next_player(current_slot)
@@ -292,7 +284,7 @@ class Actions(object):
def set_playing_sample_end_to_current_duration(self):
current_bank, current_slot = self.data.split_bankslot_number(self.video_driver.current_player.bankslot_number)
current_position = round(self.video_driver.current_player.get_position(),0)
current_position = round(self.video_driver.current_player.get_position(), 0)
self.data.update_slot_end_to_this_time(current_slot, current_position)
self._load_this_slot_into_next_player(current_slot)
@@ -305,19 +297,18 @@ class Actions(object):
is_previewing = self.capture.is_previewing
if is_previewing:
self.capture.stop_preview()
#if self.video_driver.current_player.status == 'PAUSED':
#self.video_driver.current_player.toggle_pause()
# if self.video_driver.current_player.status == 'PAUSED':
# self.video_driver.current_player.toggle_pause()
else:
is_successful = self.capture.start_preview()
#if is_successful and self.video_driver.current_player.status != 'PAUSED':
#self.video_driver.current_player.toggle_pause()
# if is_successful and self.video_driver.current_player.status != 'PAUSED':
# self.video_driver.current_player.toggle_pause()
def toggle_capture_recording(self):
is_recording = self.capture.is_recording
if is_recording:
self.capture.stop_recording()
else:
else:
self.capture.start_recording()
def toggle_screen_mirror(self):
@@ -360,12 +351,12 @@ class Actions(object):
self.data.detour_active = True
shader_input = self.data.settings['detour']['SHADER_POSITION']['value'] == 'input'
self.video_driver.osc_client.send_message("/detour/start", shader_input)
self.load_this_detour_shader()
self.load_this_detour_shader()
def toggle_detour_play(self):
if self.data.settings['detour']['TRY_DEMO']['value'] == 'enabled':
is_playing = not self.data.detour_settings['is_playing']
self.data.detour_settings['is_playing'] = is_playing
is_playing = not self.data.detour_settings['is_playing']
self.data.detour_settings['is_playing'] = is_playing
self.video_driver.osc_client.send_message("/detour/is_playing", is_playing)
def toggle_feedback(self):
@@ -432,14 +423,14 @@ class Actions(object):
def toggle_detour_record(self):
if self.data.settings['detour']['TRY_DEMO']['value'] == 'enabled':
is_recording = not self.data.detour_settings['is_recording']
self.data.detour_settings['is_recording'] = is_recording
is_recording = not self.data.detour_settings['is_recording']
self.data.detour_settings['is_recording'] = is_recording
self.video_driver.osc_client.send_message("/detour/is_recording", is_recording)
def toggle_detour_record_loop(self):
if self.data.settings['detour']['TRY_DEMO']['value'] == 'enabled':
record_loop = not self.data.detour_settings['record_loop']
self.data.detour_settings['record_loop'] = record_loop
self.data.detour_settings['record_loop'] = record_loop
self.video_driver.osc_client.send_message("/detour/record_loop", record_loop)
def clear_this_detour(self):
@@ -477,10 +468,9 @@ class Actions(object):
def switch_to_this_detour(self, number):
if self.data.settings['detour']['TRY_DEMO']['value'] == 'enabled':
self.data.detour_settings['current_detour'] = number
self.data.detour_settings['current_detour'] = number
self.video_driver.osc_client.send_message("/detour/switch_to_detour_number", number)
def set_detour_delay_mode(self, state):
self.video_driver.osc_client.send_message("/detour/set_delay_mode", state == 'enabled')
self.data.update_conjur_delay_mode(state == 'enabled')
@@ -513,19 +503,19 @@ class Actions(object):
self.set_detour_mix_continuous(1)
def set_the_camera_colour_u_continuous(self, amount):
self.capture.set_colour(amount*255, None)
self.capture.set_colour(amount * 255, None)
def set_the_camera_colour_v_continuous(self, amount):
self.capture.set_colour(None, amount*255)
self.capture.set_colour(None, amount * 255)
def set_the_camera_alpha_continuous(self, amount):
self.capture.set_alpha(amount*255)
self.capture.set_alpha(amount * 255)
def set_the_current_video_alpha_continuous(self, amount):
self.video_driver.current_player.set_alpha_value(amount*255)
self.video_driver.current_player.set_alpha_value(amount * 255)
def set_the_next_video_alpha_continuous(self, amount):
self.video_driver.next_player.set_alpha_value(amount*255)
self.video_driver.next_player.set_alpha_value(amount * 255)
def set_the_shader_param_0_layer_offset_0_continuous(self, amount):
self.shaders.set_param_to_amount(0, amount, layer_offset=0)
@@ -583,8 +573,8 @@ class Actions(object):
self.data.update_setting_value('shader', 'STROBE_AMOUNT', scaled_amount)
def get_midi_status(self):
device_name = 'none' if not hasattr(self.data,'midi_device_name') else self.data.midi_device_name
self.message_handler.set_message('INFO', ("midi status is {} to %s"%(device_name)).format(self.data.midi_status))
device_name = 'none' if not hasattr(self.data, 'midi_device_name') else self.data.midi_device_name
self.message_handler.set_message('INFO', ("midi status is {} to %s" % (device_name)).format(self.data.midi_status))
def cycle_midi_port_index(self):
self.data.midi_port_index = self.data.midi_port_index + 1
@@ -629,24 +619,23 @@ class Actions(object):
self.data.update_setting_value('video', 'OUTPUT', 'hdmi')
if self.data.settings['video']['HDMI_MODE']['value'] == "CEA 4 HDMI":
self.data.update_setting_value('video', 'HDMI_MODE', 'CEA 4 HDMI')
self.change_hdmi_settings('CEA 4 HDMI')
def check_dev_mode(self):
#### check if in dev mode:(ie not using the lcd screen)
with open('/boot/config.txt', 'r') as config:
if '##no_waveshare_overlay' in config.read():
self.data.update_setting_value('system','DEV_MODE_RESET', 'on')
else:
self.data.update_setting_value('system','DEV_MODE_RESET', 'off')
if '##no_waveshare_overlay' in config.read():
self.data.update_setting_value('system', 'DEV_MODE_RESET', 'on')
else:
self.data.update_setting_value('system', 'DEV_MODE_RESET', 'off')
def check_if_should_start_openframeworks(self):
if self.data.settings['video']['VIDEOPLAYER_BACKEND']['value'] != 'omxplayer':
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)
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):
@@ -669,7 +658,7 @@ class Actions(object):
self.set_capture_object('nothing')
self.display.settings_menu.generate_settings_list()
self.reset_players()
def reset_players(self):
self.video_driver.reset_all_players()
@@ -680,16 +669,15 @@ class Actions(object):
progressive = ''
if self.data.settings['video']['COMPOSITE_PROGRESSIVE']['value'] == 'on':
progressive = 'p'
if output == 'composite':
subprocess.call(['tvservice --sdtvon="{} {} {}"'.format(mode, aspect, progressive)],shell=True)
subprocess.call(['tvservice --sdtvon="{} {} {}"'.format(mode, aspect, progressive)], shell=True)
self.refresh_frame_buffer_and_restart_openframeworks()
self.persist_composite_setting(mode, progressive, aspect)
def _refresh_frame_buffer(self):
self.data.open_omxplayer_for_reset()
subprocess.run(["fbset -depth 16; fbset -depth 32; xrefresh -display :0" ], shell=True)
subprocess.run(["fbset -depth 16; fbset -depth 32; xrefresh -display :0"], shell=True)
def persist_composite_setting(self, mode, progressive, aspect):
sdtv_mode = ''
@@ -714,7 +702,7 @@ class Actions(object):
self.update_config_settings(sdtv_mode, sdtv_aspect)
def update_config_settings(self, sdtv_mode, sdtv_aspect):
self.run_script('set_composite_mode',sdtv_mode, sdtv_aspect)
self.run_script('set_composite_mode', sdtv_mode, sdtv_aspect)
def switch_dev_mode(self, state):
if state == 'on':
@@ -727,13 +715,12 @@ class Actions(object):
self.switch_display_to_lcd()
def switch_display_to_hdmi(self):
with open('/boot/config.txt', 'r') as config:
with open('/boot/config.txt', 'r') as config:
with open('/usr/share/X11/xorg.conf.d/99-fbturbo.conf') as framebuffer_conf:
if 'dtoverlay=waveshare35a:rotate=270' in config.read() and 'dev/fb1' in framebuffer_conf.read():
self.run_script('switch_display_to_hdmi')
else:
self.message_handler.set_message('INFO', 'failed to switch display')
def switch_display_to_lcd(self):
with open('/boot/config.txt', 'r') as config:
@@ -745,9 +732,9 @@ class Actions(object):
self.message_handler.set_message('INFO', 'failed to switch display')
def run_script(self, script_name, first_argument='', second_argument=''):
print('first arg is {} , second is {}'.format(first_argument,second_argument))
subprocess.call(['/home/pi/r_e_c_u_r/dotfiles/{}.sh'.format(script_name),first_argument, second_argument ])
print('first arg is {} , second is {}'.format(first_argument, second_argument))
subprocess.call(['/home/pi/r_e_c_u_r/dotfiles/{}.sh'.format(script_name), first_argument, second_argument])
def toggle_x_autorepeat(self):
if self.data.auto_repeat_on:
subprocess.call(['xset', 'r', 'off'])
@@ -756,13 +743,12 @@ class Actions(object):
subprocess.call(['xset', 'r', 'on'])
self.data.auto_repeat_on = True
def quit_the_program(self):
self.data._update_json(self.data.SETTINGS_JSON, self.data.settings)
self.data.plugins.quit_plugins()
self.video_driver.exit_all_players()
self.exit_openframeworks()
self.exit_osc_server('','')
self.exit_osc_server('', '')
self.create_client_and_shutdown_osc_server()
self.stop_serial_port_process()
self.stop_openframeworks_process()
@@ -772,8 +758,8 @@ class Actions(object):
def restart_the_program(self):
self.quit_the_program()
os.execv('/usr/bin/python3', [sys.argv[0],'/home/pi/r_e_c_u_r/r_e_c_u_r.py'])
os.execv('/usr/bin/python3', [sys.argv[0], '/home/pi/r_e_c_u_r/r_e_c_u_r.py'])
def set_shader_param_mode(self):
self.data.control_mode = 'SHADER_PARAM'
self.message_handler.set_message('INFO', '[ ]: focus < >: level ■: back')
@@ -786,21 +772,21 @@ class Actions(object):
self.shaders.decrease_this_param(self.data.settings['shader']['SHADER_PARAM']['value'])
def increase_param_focus(self):
self.shaders.focused_param = (self.shaders.focused_param + 1)%self.shaders.selected_shader_list[self.data.shader_layer]['param_number']
self.shaders.focused_param = (self.shaders.focused_param + 1) % self.shaders.selected_shader_list[self.data.shader_layer]['param_number']
def decrease_param_focus(self):
self.shaders.focused_param = (self.shaders.focused_param - 1)%self.shaders.selected_shader_list[self.data.shader_layer]['param_number']
self.shaders.focused_param = (self.shaders.focused_param - 1) % self.shaders.selected_shader_list[self.data.shader_layer]['param_number']
def increase_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.update_setting_value('shader', 'SHADER_PARAM', options[(current_index + 1) % len(options) ])
current_index = [index for index, item in enumerate(options) if item == self.data.settings['shader']['SHADER_PARAM']['value']][0]
self.data.update_setting_value('shader', 'SHADER_PARAM', options[(current_index + 1) % len(options)])
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.update_setting_value('shader', 'SHADER_PARAM', options[(current_index - 1) % len(options) ])
current_index = [index for index, item in enumerate(options) if item == self.data.settings['shader']['SHADER_PARAM']['value']][0]
self.data.update_setting_value('shader', 'SHADER_PARAM', options[(current_index - 1) % len(options)])
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):
@@ -808,12 +794,11 @@ class Actions(object):
self.message_handler.set_message('INFO', 'tap: ■ ; < > : back')
self.fixed_length_setter = length_setter.FixedLengthSetter(self.data)
def return_to_default_control_mode(self):
display_list = self.data.get_display_modes_list(with_nav_mode=True)
for display, control in display_list:
if display == self.data.display_mode:
self.data.control_mode = control
self.data.control_mode = control
def perform_confirm_action(self):
action = self.data.confirm_action
@@ -829,10 +814,10 @@ class Actions(object):
self.message_handler.set_message('INFO', 'confirm: {} ■:y < >:no'.format(action_title[:22]))
def confirm_shutdown(self):
self.start_confirm_action('shutdown_pi' )
self.start_confirm_action('shutdown_pi')
def confirm_quit(self):
self.start_confirm_action('quit_the_program', message='quit' )
self.start_confirm_action('quit_the_program', message='quit')
def confirm_switch_dev_mode(self, state):
# i startd writing a confirm dev mod but it messed with the state if you say no ...
@@ -843,7 +828,6 @@ class Actions(object):
self.fixed_length_setter.record_input()
self.display.settings_menu.generate_settings_list()
def setup_osc_server(self):
server_parser = argparse.ArgumentParser()
server_parser.add_argument("--ip", default="127.0.0.1", help="the ip")
@@ -861,7 +845,7 @@ class Actions(object):
this_dispatcher.map("/detour/detour_info", self.receive_detour_info)
this_dispatcher.map("/capture/recording_finished", self.capture.receive_recording_finished)
this_dispatcher.map("/shutdown", self.exit_osc_server)
#this_dispatcher.map("/player/a/status", self.set_status)
# this_dispatcher.map("/player/a/status", self.set_status)
osc_server.ThreadingOSCUDPServer.allow_reuse_address = True
server = osc_server.ThreadingOSCUDPServer((server_args.ip, server_args.port), this_dispatcher)
@@ -889,10 +873,10 @@ class Actions(object):
def toggle_access_point(self, setting_value):
osc_setting_state = self.data.settings['user_input']['OSC_INPUT']['value']
self.data.update_setting_value('user_input', 'OSC_INPUT', 'disabled')
self.data.update_setting_value('user_input', 'OSC_INPUT', 'disabled')
self.tk.after(2000, self.toggle_access_point_delay, setting_value, osc_setting_state)
def toggle_access_point_delay(self, setting_value, osc_setting_state ):
def toggle_access_point_delay(self, setting_value, osc_setting_state):
if setting_value == 'enabled':
subprocess.call(['sudo', 'bash', '/home/pi/raspiApWlanScripts/switchToAP.sh'])
else:
@@ -914,7 +898,6 @@ class Actions(object):
def enable_osc(self, osc_setting_state):
self.data.update_setting_value('user_input', 'OSC_INPUT', osc_setting_state)
def show_ip(self, *args):
address = self.data.get_ip_address()
self.message_handler.set_message('INFO', 'ip is {}:8080'.format(address))
@@ -922,14 +905,13 @@ class Actions(object):
def create_serial_port_process(self):
if self.serial_port_process == None:
self.serial_port_process = subprocess.Popen("exec " + "ttymidi -s /dev/serial0 -b 38400 -n serial", shell=True)
print('created the serial port process ? {}'.format(self.serial_port_process))
print('created the serial port process ? {}'.format(self.serial_port_process))
def stop_serial_port_process(self):
if self.serial_port_process is not None:
self.serial_port_process.kill()
self.serial_port_process = None
def stop_remote_process(self):
if self.remote_process is not None:
self.remote_process.kill()
@@ -939,7 +921,7 @@ class Actions(object):
self.reset_players()
self.exit_openframeworks()
self.stop_openframeworks_process()
self.check_if_should_start_openframeworks()
self.check_if_should_start_openframeworks()
def refresh_frame_buffer_and_restart_openframeworks(self):
if self.data.settings['video']['VIDEOPLAYER_BACKEND']['value'] != 'omxplayer':
@@ -948,7 +930,7 @@ class Actions(object):
self.stop_openframeworks_process()
self._refresh_frame_buffer()
self.check_if_should_start_openframeworks()
#self.tk.after(1000, self.check_if_should_start_openframeworks)
# self.tk.after(1000, self.check_if_should_start_openframeworks)
else:
self._refresh_frame_buffer()
@@ -960,21 +942,21 @@ class Actions(object):
subprocess.call(['killall', 'c_o_n_j_u_r'])
def try_pull_code_and_reset(self):
#self.message_handler.set_message('INFO', 'checkin fo updates pls wait')
# self.message_handler.set_message('INFO', 'checkin fo updates pls wait')
recur_repo = git.Repo("~/r_e_c_u_r")
conjur_repo = git.Repo(self.data.PATH_TO_OPENFRAMEWORKS + "apps/myApps/c_o_n_j_u_r")
ofxVideoArtTools_repo = git.Repo(self.data.PATH_TO_OPENFRAMEWORKS + "/addons/ofxVideoArtTools")
ofxVideoArtTools_repo = git.Repo(self.data.PATH_TO_OPENFRAMEWORKS + "/addons/ofxVideoArtTools")
current_recur_hash = recur_repo.head.object.hexsha
current_conjur_hash = conjur_repo.head.object.hexsha
current_ofxVideoArtTools_hash = ofxVideoArtTools_repo.head.object.hexsha
self.data.try_remove_file(self.data.PATH_TO_DATA_OBJECTS + self.data.SETTINGS_JSON )
self.data.try_remove_file(self.data.PATH_TO_DEFAULT_CONJUR_DATA)
self.data.try_remove_file(self.data.PATH_TO_DATA_OBJECTS + self.data.SETTINGS_JSON)
self.data.try_remove_file(self.data.PATH_TO_DEFAULT_CONJUR_DATA)
try:
recur_repo.remotes.origin.pull()
conjur_repo.remotes.origin.pull()
ofxVideoArtTools_repo.remotes.origin.pull()
except git.exc.GitCommandError as e:
except git.exc.GitCommandError as e:
if 'unable to access' in str(e):
self.message_handler.set_message('INFO', 'not connected to network')
else:
@@ -982,22 +964,22 @@ class Actions(object):
error_info = e.message
else:
error_info = e
self.message_handler.set_message('ERROR',error_info)
self.message_handler.set_message('ERROR', error_info)
return
new_recur_hash = recur_repo.head.object.hexsha
new_conjur_hash = conjur_repo.head.object.hexsha
new_ofxVideoArtTools_hash = ofxVideoArtTools_repo.head.object.hexsha
if current_recur_hash != new_recur_hash or current_conjur_hash != new_conjur_hash or current_ofxVideoArtTools_hash != new_ofxVideoArtTools_hash :
#something has changed!
if current_recur_hash != new_recur_hash or current_conjur_hash != new_conjur_hash or current_ofxVideoArtTools_hash != new_ofxVideoArtTools_hash:
# something has changed!
self.restart_the_program()
else:
self.message_handler.set_message('INFO', 'up to date !')
# def complie_openframeworks(self):
# subprocess.call(['make', '--directory=' + self.data.PATH_TO_OPENFRAMEWORKS + 'apps/myApps/c_o_n_j_u_r' ])
# self.message_handler.set_message('INFO', 'finished compiling!')
# self.restart_the_program()
# def complie_openframeworks(self):
# subprocess.call(['make', '--directory=' + self.data.PATH_TO_OPENFRAMEWORKS + 'apps/myApps/c_o_n_j_u_r' ])
# self.message_handler.set_message('INFO', 'finished compiling!')
# self.restart_the_program()
def shutdown_pi(self):
subprocess.call(['sudo', 'shutdown', '-h', 'now'])
@@ -1005,7 +987,7 @@ class Actions(object):
def clear_message(self):
self.message_handler.clear_all_messages()
#"""def modulate_param_layer_offset_to_amount(self, param, layer, amount):
# """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
@@ -1016,35 +998,34 @@ class Actions(object):
def eject_all_usb_drives(self):
for i in range(1, 4):
if os.path.exists('/dev/sda{}'.format(i)):
subprocess.call(['sudo', 'eject', '/dev/sda{}'.format(i)])
subprocess.call(['sudo', 'eject', '/dev/sda{}'.format(i)])
self.message_handler.set_message('INFO', 'usb ejected')
# TODO: make this interrogate the various components for available routes to parse
# this would include eg a custom script module..
@property
def parserlist(self):
return {
( r"^play_shader_([0-9])_([0-9])$", self.shaders.play_that_shader ),
( 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"^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_modulation_level ),
( r"^set_param_([0-3])_layer_offset_([0-2])_modulation_level_continuous$", self.shaders.set_param_layer_offset_modulation_level ),
( r"^reset_selected_modulation$", self.shaders.reset_selected_modulation ),
( r"^reset_modulation_([0-3])$", self.shaders.reset_modulation ),
( r"^select_shader_modulation_slot_([0-3])$", self.shaders.select_shader_modulation_slot ),
( r"^select_next_shader_modulation_slot$", self.shaders.select_next_shader_modulation_slot ),
( r"^select_previous_shader_modulation_slot$", self.shaders.select_previous_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 )
(r"^play_shader_([0-9])_([0-9])$", self.shaders.play_that_shader),
(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"^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_modulation_level),
(r"^set_param_([0-3])_layer_offset_([0-2])_modulation_level_continuous$", self.shaders.set_param_layer_offset_modulation_level),
(r"^reset_selected_modulation$", self.shaders.reset_selected_modulation),
(r"^reset_modulation_([0-3])$", self.shaders.reset_modulation),
(r"^select_shader_modulation_slot_([0-3])$", self.shaders.select_shader_modulation_slot),
(r"^select_next_shader_modulation_slot$", self.shaders.select_next_shader_modulation_slot),
(r"^select_previous_shader_modulation_slot$", self.shaders.select_previous_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):
a = [ int(arg) if str(arg).isnumeric() else str(arg) for arg in list(args) ]
a = [int(arg) if str(arg).isnumeric() else str(arg) for arg in list(args)]
return a
def get_callback_for_method(self, method_name, argument):
@@ -1054,13 +1035,13 @@ class Actions(object):
matches = re.search(regex, method_name)
if matches:
found_method = me
parsed_args = self.detect_types(matches.groups()) #list(map(int,matches.groups()))
found_method = me
parsed_args = self.detect_types(matches.groups()) # list(map(int,matches.groups()))
if argument is not None:
args = parsed_args + [argument]
else:
args = parsed_args
args = parsed_args
return (found_method, args)
return None, None
@@ -1072,7 +1053,7 @@ class Actions(object):
# first check if we have a native method to handle this for us
# todo: assess whether it would still be performant/desirable to be able to override core actions with plugins?
if hasattr(self, method_name):
method = getattr(self, method_name)
if argument is not None:
arguments = [argument]
@@ -1089,29 +1070,28 @@ class Actions(object):
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))
print("Plugin %s is handling %s" % (plugin, method_name))
method, arguments = plugin.get_callback_for_method(method_name, argument)
break # only deal with the first plugin
break # only deal with the first plugin
if method is None:
print ("Failed to find a method for '%s'" % method_name)
print("Failed to find a method for '%s'" % method_name)
import traceback
traceback.print_exc()
return
try:
#print ("for method_name %s, arguments is %s and len is %s, got method %s" % (method_name, arguments, len(signature(method).parameters), method))
# for the case where cc is being used as switch, we ignore note_off
#print(type(argument))
# print ("for method_name %s, arguments is %s and len is %s, got method %s" % (method_name, arguments, len(signature(method).parameters), method))
# for the case where cc is being used as switch, we ignore note_off
# print(type(argument))
if len(signature(method).parameters) == 0 and isinstance(argument, float) and argument == 0:
print('cc off ?')
return
if arguments is not None and len(signature(method).parameters)==len(arguments): # only pass arguments if count matches method sig
if arguments is not None and len(signature(method).parameters) == len(arguments): # only pass arguments if count matches method sig
method(*arguments)
else:
method()
except:
print ("Exception calling action for '%s' with arguments ( %s ) " % ( method_name, arguments))
print("Exception calling action for '%s' with arguments ( %s ) " % (method_name, arguments))
import traceback
traceback.print_exc()

View File

@@ -1,17 +1,17 @@
import subprocess
import json
import xml.etree.ElementTree as ET
import os
import collections
import json
import os
import subprocess
import threading
import xml.etree.ElementTree as ET
from random import randint
import inspect
from itertools import cycle
from omxplayer.player import OMXPlayer
from shutil import copyfile
import threading
from omxplayer.player import OMXPlayer
from data_centre import plugin_collection
class AsyncWrite(threading.Thread):
def __init__(self, filename, data, mode='json'):
threading.Thread.__init__(self)
@@ -21,7 +21,7 @@ class AsyncWrite(threading.Thread):
def run(self):
with open(self.filename, "w+") as data_file:
if self.mode=='json':
if self.mode == 'json':
json.dump(self.data, data_file, indent=4, sort_keys=True)
else:
data_file.write(self.data)
@@ -29,7 +29,6 @@ class AsyncWrite(threading.Thread):
class Data(object):
BANK_DATA_JSON = 'display_data.json'
SHADER_BANK_DATA_JSON = 'shader_bank_data.json'
SETTINGS_JSON = 'settings.json'
@@ -48,11 +47,11 @@ class Data(object):
def __init__(self, message_handler):
self.message_handler = message_handler
#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 ]
# 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]
### state data
self.auto_repeat_on = True
@@ -66,20 +65,20 @@ class Data(object):
self.update_screen = True
self.confirm_action = None
self.player_mode = 'now'
self.feedback_active = False
self.detour_active = False
self.detour_mix_shaders = self.get_list_of_two_input_shaders()
self.detour_settings = collections.OrderedDict([('current_detour',0), ('is_playing', False), ('is_recording', False), ('record_loop', False), ('detour_size', False), ('detour_speed', 0), ('memory_full', False), ('mix_shader', self.detour_mix_shaders[0]), ('detour_position', 5), ('detour_start', 0), ('detour_end', 0), ('detour_mix', 0), ('is_delay', False)])
self.detour_settings = collections.OrderedDict([('current_detour', 0), ('is_playing', False), ('is_recording', False), ('record_loop', False), ('detour_size', False), ('detour_speed', 0), ('memory_full', False), ('mix_shader', self.detour_mix_shaders[0]), ('detour_position', 5), ('detour_start', 0), ('detour_end', 0), ('detour_mix', 0), ('is_delay', False)])
self.next_bankslot = '0-0'
self.current_bankslot = '0-0'
self.shader_layer = 0
### persisted data (use default if doesnt exits):
if not os.path.isfile(self.PATH_TO_CONJUR_DATA):
self.try_remove_file(self.PATH_TO_DATA_OBJECTS + self.SETTINGS_JSON ) # keep the, in sync
self.try_remove_file(self.PATH_TO_DATA_OBJECTS + self.SETTINGS_JSON) # keep the, in sync
copyfile(self.PATH_TO_DEFAULT_CONJUR_DATA, self.PATH_TO_CONJUR_DATA)
self.bank_data = [self.create_empty_bank()]
@@ -99,7 +98,7 @@ class Data(object):
if os.path.isfile(self.PATH_TO_DATA_OBJECTS + self.SETTINGS_JSON):
self.settings = self._read_json(self.SETTINGS_JSON)
self.settings['user_input'].setdefault('REMOTE_SERVER',
self.default_settings['user_input']['REMOTE_SERVER'])['value'] = 'disabled' # remote server off at boot
self.default_settings['user_input']['REMOTE_SERVER'])['value'] = 'disabled' # remote server off at boot
self.key_mappings = self._read_json(self.KEYPAD_MAPPING_JSON)
self.osc_mappings = self._read_json(self.OSC_MAPPING_JSON)
@@ -113,15 +112,15 @@ class Data(object):
docs.generate_mappings_doc("OSC mappings", self.osc_mappings, column_one_header="OSC address")
docs.generate_mappings_doc("Analog mappings", self.analog_mappings, column_one_header="Analogue input")
docs.generate_mappings_doc("Key mappings", self.key_mappings, column_one_header="Key mappings")
#quit()
# quit()
def initialise_plugins(self):
#initialise plugin manager
# initialise plugin manager
self.plugins = plugin_collection.PluginCollection("plugins", self.message_handler, self)
self.compare_plugins_list()
def get_enabled_plugin_class_names(self):
return [k for k,v in self.enabled_plugins.items() if v is True]
return [k for k, v in self.enabled_plugins.items() if v is True]
def compare_plugins_list(self):
current_plugins = [type(plugin).__name__ for plugin in self.plugins.get_plugins(include_disabled=True)]
@@ -131,9 +130,9 @@ class Data(object):
self.enabled_plugins.pop(k, None)
for k in plugins_to_add:
self.enabled_plugins[k] = False
#switch off all plugins if disabled ...
# switch off all plugins if disabled ...
if self.settings['system']['USE_PLUGINS']['value'] == 'disabled':
self.enabled_plugins = {x:False for x in self.enabled_plugins}
self.enabled_plugins = {x: False for x in self.enabled_plugins}
def update_enabled_plugins(self, key, value):
self.enabled_plugins[key] = value
@@ -141,16 +140,16 @@ class Data(object):
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)
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))
print ("loaded custom midi mapping for %s" % custom_file)
print("loaded custom midi mapping for %s" % custom_file)
else:
print ("loading default midi mapping for %s" % (device_name))
print("loading default midi mapping for %s" % (device_name))
self.midi_mappings = self._read_json(self.MIDI_MAPPING_JSON)
return self.midi_mappings
def get_ip_address(self):
ip_list = subprocess.check_output(['hostname', '-I']).decode('utf-8').split()
if len(ip_list) > 0:
@@ -163,7 +162,7 @@ class Data(object):
return '127.0.0.1'
else:
return self.get_ip_address()
@staticmethod
def create_empty_bank():
empty_slot = dict(name='', location='', length=-1, start=-1, end=-1, rate=1)
@@ -173,7 +172,7 @@ class Data(object):
def create_empty_shader_bank():
empty_slot = dict(name='', path='', param_number=4, shad_type='-')
return [empty_slot for i in range(10)]
def _read_json(self, file_name):
with open(self.PATH_TO_DATA_OBJECTS + file_name) as data_file:
data = json.load(data_file)
@@ -187,15 +186,15 @@ class Data(object):
for path in self.PATHS_TO_PLUGIN_DATA:
print("loading plugin data %s" % path)
try:
with open("%s/%s" % (path,file_name)) as data_file:
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)
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:
# 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()
@@ -220,12 +219,12 @@ class Data(object):
tree.write(self.PATH_TO_CONJUR_DATA)
def get_setting_and_folder_from_name(self, setting_name):
for folder_key , folder_item in self.settings.items():
for folder_key, folder_item in self.settings.items():
for setting_key, setting_item in folder_item.items():
if setting_key == setting_name:
return folder_key, setting_key, setting_item
##### setting and adding to sample mapping ######
##### setting and adding to sample mapping ######
def create_new_slot_mapping_in_first_open(self, file_name):
######## used for mapping current video to next available slot ########
@@ -238,7 +237,7 @@ class Data(object):
def create_new_slot_mapping(self, slot_number, file_name):
######## used for mapping current video to a specific slot ########
has_location, location = self._get_path_for_file(file_name)
print('file_name:{},has_location:{}, location:{}'.format(file_name,has_location, location))
print('file_name:{},has_location:{}, location:{}'.format(file_name, has_location, location))
length = self._get_length_for_file(location)
if length:
new_slot = dict(name=file_name, location=location, length=length, start=-1, end=-1, rate=1)
@@ -250,15 +249,15 @@ class Data(object):
def update_bank_number_by_amount(self, amount):
empty_bank = self.create_empty_bank()
if(self.bank_data[-1] != empty_bank):
if (self.bank_data[-1] != empty_bank):
self.bank_data.append(empty_bank)
elif(len(self.bank_data) > 1):
elif (len(self.bank_data) > 1):
if self.bank_data[-2] == empty_bank:
self.bank_data.pop()
self._update_json(self.BANK_DATA_JSON, self.bank_data)
self.bank_number = (self.bank_number+amount)%(len(self.bank_data))
def update_next_slot_number(self, new_value, is_current=False):
self.bank_number = (self.bank_number + amount) % (len(self.bank_data))
def update_next_slot_number(self, new_value, is_current=False):
if self.bank_data[self.bank_number][new_value]['location'] == '':
self.message_handler.set_message('INFO', 'the slot you pressed is empty')
return False
@@ -266,14 +265,13 @@ class Data(object):
self.message_handler.set_message('INFO', 'no device found for this slot')
return False
elif is_current:
self.current_bankslot = '{}-{}'.format(self.bank_number,new_value)
self.current_bankslot = '{}-{}'.format(self.bank_number, new_value)
return True
else:
self.next_bankslot = '{}-{}'.format(self.bank_number,new_value)
self.next_bankslot = '{}-{}'.format(self.bank_number, new_value)
return True
######## setting and adding to shader mapping
######## setting and adding to shader mapping
def create_new_shader_mapping_in_first_open(self, file_name):
######## used for mapping current shader to next available slot ########
@@ -286,7 +284,7 @@ class Data(object):
def create_new_shader_mapping(self, slot_number, file_name):
######## used for mapping current shader to a specific slot ########
has_location, location = self._get_path_for_file(file_name)
print('file_name:{},has_location:{}, location:{}'.format(file_name,has_location, location))
print('file_name:{},has_location:{}, location:{}'.format(file_name, has_location, location))
new_slot = dict(name=file_name, path=location, shad_type='-', param_number=4)
self._update_a_shader_slots_data(slot_number, new_slot)
@@ -295,28 +293,26 @@ class Data(object):
self._update_json(self.SHADER_BANK_DATA_JSON, self.shader_bank_data)
def update_shader_layer_by_amount(self, amount):
self.shader_layer = (self.shader_layer + amount) % len(self.shader_bank_data)
self.shader_layer = (self.shader_layer + amount) % len(self.shader_bank_data)
def update_setting_value(self, setting_folder, setting_name, setting_value):
self.settings[setting_folder][setting_name]['value'] = setting_value
self._update_json(self.SETTINGS_JSON, self.settings)
return self.settings[setting_folder][setting_name]
@classmethod
def split_bankslot_number(cls, bankslot_number):
split = bankslot_number.split('-')
is_bank_num_int , converted_bank_number = cls.try_convert_string_to_int(split[0])
is_slot_num_int , converted_slot_number = cls.try_convert_string_to_int(split[1])
is_bank_num_int, converted_bank_number = cls.try_convert_string_to_int(split[0])
is_slot_num_int, converted_slot_number = cls.try_convert_string_to_int(split[1])
return converted_bank_number, converted_slot_number
@staticmethod
def try_convert_string_to_int(string_input):
try:
return True , int(string_input)
return True, int(string_input)
except ValueError:
return False , '*'
return False, '*'
def get_next_context(self, is_current=False):
######## loads the slot details, uses settings to modify them and then set next slot number ########
@@ -324,14 +320,14 @@ class Data(object):
bankslot_number = self.current_bankslot
else:
bankslot_number = self.next_bankslot
bank_num , slot_num = self.split_bankslot_number(bankslot_number)
bank_num, slot_num = self.split_bankslot_number(bankslot_number)
next_slot_details = self.bank_data[bank_num][slot_num]
start_value = next_slot_details['start']
end_value = next_slot_details['end']
length = next_slot_details['length']
start_value, end_value = self._overwrite_values_with_sampler_settings(start_value, end_value, length)
start_value, end_value = self._overwrite_values_with_sampler_settings(start_value, end_value, length)
context = dict(location=next_slot_details['location'], name=next_slot_details['name'],
length=next_slot_details['length'], rate=next_slot_details['rate'], start=start_value, end=end_value,
@@ -347,22 +343,22 @@ class Data(object):
fixed_length_multiply = self.settings['sampler']['FIXED_LENGTH_MULTIPLY']['value']
total_fixed_length = fixed_length_value * fixed_length_multiply
if start == -1:
start = 0
start = 0
if end == -1:
end = length
end = length
new_end = end
new_start = start
if use_fixed_length and use_rand_start:
max_increase = int(max(end - start - max(total_fixed_length, 4),0))
random_increase = randint(0,max_increase)
max_increase = int(max(end - start - max(total_fixed_length, 4), 0))
random_increase = randint(0, max_increase)
new_start = start + random_increase
new_end = min(new_start + total_fixed_length, end)
elif use_fixed_length and not use_rand_start:
new_end = min(new_start + total_fixed_length, end)
elif not use_fixed_length and use_rand_start:
max_increase = int(max(end - start - 4,0))
random_increase = randint(0,max_increase)
max_increase = int(max(end - start - 4, 0))
random_increase = randint(0, max_increase)
new_start = start + random_increase
return new_start, new_end
@@ -372,17 +368,17 @@ class Data(object):
loaded_slots = self._get_list_of_loaded_slots_in_current_bank()
if loaded_slots:
if next_setting == 'random':
next_slot = loaded_slots[randint(0,len(loaded_slots)-1)]
next_slot = loaded_slots[randint(0, len(loaded_slots) - 1)]
elif next_setting == 'consecutive':
next_slot = self.get_next_loaded_slot(slot_num, loaded_slots)
else:
next_slot = slot_num
if is_current:
self.current_bankslot = '{}-{}'.format(self.bank_number,next_slot)
self.current_bankslot = '{}-{}'.format(self.bank_number, next_slot)
else:
self.next_bankslot = '{}-{}'.format(self.bank_number,next_slot)
self.next_bankslot = '{}-{}'.format(self.bank_number, next_slot)
def _get_list_of_loaded_slots_in_current_bank(self):
list_of_loaded_slots = []
for index, slot in enumerate(self.bank_data[self.bank_number]):
@@ -392,9 +388,9 @@ class Data(object):
@staticmethod
def get_next_loaded_slot(current_slot, loaded_slots):
i = ( current_slot + 1 ) % len(loaded_slots)
while(i not in loaded_slots):
i = ( i + 1 ) % len(loaded_slots)
i = (current_slot + 1) % len(loaded_slots)
while (i not in loaded_slots):
i = (i + 1) % len(loaded_slots)
return i
def update_slot_start_to_this_time(self, slot_number, position):
@@ -410,7 +406,7 @@ class Data(object):
self._update_json(self.BANK_DATA_JSON, self.bank_data)
def open_omxplayer_for_reset(self):
self._get_length_for_file('/ss',no_message=True )
self._get_length_for_file('/ss', no_message=True)
def _get_length_for_file(self, path, no_message=False):
try:
@@ -419,62 +415,60 @@ class Data(object):
temp_player.quit()
return duration
except Exception as e:
print (e)
print(e)
if not no_message:
self.message_handler.set_message('INFO', 'cannot load video')
return None
def _get_path_for_file(self, file_name):
######## returns full path for a given file name ########
for path in self.PATHS_TO_BROWSER + self.PATHS_TO_SHADERS:
for path in self.PATHS_TO_BROWSER + self.PATHS_TO_SHADERS:
for root, dirs, files in os.walk(path):
if file_name in files:
return True, '{}/{}'.format(root, file_name)
return False, ''
def is_this_path_broken(self, path):
external_devices = os.listdir(self.PATH_TO_EXTERNAL_DEVICES)
has_device_in_path = self.PATH_TO_EXTERNAL_DEVICES in path
has_existing_device_in_path = any([(x in path) for x in external_devices])
if has_device_in_path and not has_existing_device_in_path:
if has_device_in_path and not has_existing_device_in_path:
return True
else:
return False
def get_background_colour(self):
colour_name = self.settings['video']['BACKGROUND_COLOUR']['value']
colour_argb = (255,0,0,0)
colour_argb = (255, 0, 0, 0)
if colour_name == "black":
colour_argb = (255,0,0,0)
colour_argb = (255, 0, 0, 0)
elif colour_name == "white":
colour_argb = (255,255,255,255)
colour_argb = (255, 255, 255, 255)
elif colour_name == "green":
colour_argb = (255,0,255,0)
colour_argb = (255, 0, 255, 0)
elif colour_name == "blue":
colour_argb = (255,0,0,255)
colour_argb = (255, 0, 0, 255)
elif colour_name == "pink":
colour_argb = (255,255,0,255)
colour_argb = (255, 255, 0, 255)
elif colour_name == "none":
colour_argb = (0,0,0,0)
colour_argb = (0, 0, 0, 0)
colour_hex = '%02x%02x%02x%02x' % colour_argb
return colour_hex
def get_display_modes_list(self, with_nav_mode=False):
display_modes = [[ "SAMPLER",'PLAYER'], ["BROWSER",'NAV_BROWSER'],["SETTINGS",'NAV_SETTINGS']]
display_modes = [["SAMPLER", 'PLAYER'], ["BROWSER", 'NAV_BROWSER'], ["SETTINGS", 'NAV_SETTINGS']]
if self.settings['video']['VIDEOPLAYER_BACKEND']['value'] != 'omxplayer' and self.settings['shader']['USE_SHADER']['value'] == 'enabled':
display_modes.append(["SHADERS",'NAV_SHADERS'])
if self.settings['shader']['USE_SHADER_BANK']['value'] == 'enabled' and ["SHADERS",'NAV_SHADERS'] in display_modes:
display_modes.append(["SHDR_BNK",'PLAY_SHADER'])
if self.settings['shader']['USE_SHADER_MOD']['value'] == 'enabled' and ["SHADERS",'NAV_SHADERS'] in display_modes:
display_modes.append(["SHDR_MOD",["NAV_MOD","PLAY_SHADER"]]) ## allow override, but fall back to PLAY_SHADER controls
display_modes.append(["SHADERS", 'NAV_SHADERS'])
if self.settings['shader']['USE_SHADER_BANK']['value'] == 'enabled' and ["SHADERS", 'NAV_SHADERS'] in display_modes:
display_modes.append(["SHDR_BNK", 'PLAY_SHADER'])
if self.settings['shader']['USE_SHADER_MOD']['value'] == 'enabled' and ["SHADERS", 'NAV_SHADERS'] in display_modes:
display_modes.append(["SHDR_MOD", ["NAV_MOD", "PLAY_SHADER"]]) ## allow override, but fall back to PLAY_SHADER controls
if self.settings['detour']['TRY_DEMO']['value'] == 'enabled':
display_modes.append(["FRAMES",'NAV_DETOUR'])
display_modes.append(["FRAMES", 'NAV_DETOUR'])
if self.settings['system'].setdefault('USE_PLUGINS',
self.default_settings.setdefault('USE_PLUGINS',{'value': 'enabled'})).get('value') == 'enabled':
display_modes.append(["PLUGINS",'NAV_PLUGINS'])
self.default_settings.setdefault('USE_PLUGINS', {'value': 'enabled'})).get('value') == 'enabled':
display_modes.append(["PLUGINS", 'NAV_PLUGINS'])
if hasattr(self, 'plugins') and self.plugins is not None:
from data_centre.plugin_collection import DisplayPlugin
@@ -499,7 +493,7 @@ class Data(object):
######## overwrite a given slots info with new data ########
self.shader_bank_data[self.shader_layer][slot_number] = slot_info
self._update_json(self.SHADER_BANK_DATA_JSON, self.shader_bank_data)
@staticmethod
def make_empty_if_none(input):
if input is None:
@@ -512,15 +506,13 @@ class Data(object):
if os.path.exists('/home/pi/r_e_c_u_r/Shaders/2-input'):
(_, _, filenames) = next(os.walk('/home/pi/r_e_c_u_r/Shaders/2-input'))
return filenames
#elif os.path.exists('/home/pi/r_e_c_u_r/Shaders/2-input'):
#(_, _, filenames) = next(os.walk('/home/pi/r_e_c_u_r/Shaders/2-input'))
#return filenames
# elif os.path.exists('/home/pi/r_e_c_u_r/Shaders/2-input'):
# (_, _, filenames) = next(os.walk('/home/pi/r_e_c_u_r/Shaders/2-input'))
# return filenames
else:
return []
return []
@staticmethod
def try_remove_file(path):
if os.path.exists(path):
os.remove(path)

View File

@@ -1,14 +1,15 @@
import datetime
class FixedLengthSetter(object):
DELTA_NUMBER = 2
DELTA_NUMBER = 2
def __init__(self, data):
self.data = data
self.last_time = None
self.list_of_deltas = []
def record_input(self):
if self.last_time == None:
self.last_time = datetime.datetime.now()
@@ -17,6 +18,6 @@ class FixedLengthSetter(object):
self.list_of_deltas.append(now_time - self.last_time)
self.last_time = now_time
if len(self.list_of_deltas) > self.DELTA_NUMBER:
average_delta = sum(self.list_of_deltas[-self.DELTA_NUMBER+1:], datetime.timedelta(0))/float(self.DELTA_NUMBER)
average_delta = sum(self.list_of_deltas[-self.DELTA_NUMBER + 1:], datetime.timedelta(0)) / float(self.DELTA_NUMBER)
average_seconds = round(average_delta.total_seconds(), 2)
self.data.update_setting_value('sampler', 'FIXED_LENGTH', average_seconds)

View File

@@ -1,14 +1,15 @@
import inspect
import os
import pkgutil
import re
from plugins.frame_manager import FrameManager, Frame
from plugins.frame_manager import FrameManager
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
"""
@property
def disabled(self):
return type(self).__name__ not in self.pc.data.get_enabled_plugin_class_names()
@@ -23,9 +24,11 @@ class Plugin(object):
def start_plugin(self):
print(">>Starting plugin " + type(self).__name__)
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'
@@ -39,29 +42,32 @@ class MidiFeedbackPlugin(Plugin):
def refresh_midi_feedback(self):
raise NotImplementedError
class SequencePlugin(Plugin):
"""Base class for plugins that run constantly or on demand for eg automation"""
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 ),
( r"^set_automation_speed$", self.set_speed ),
(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),
(r"^set_automation_speed$", self.set_speed),
]
def set_speed(self, speed):
#self.speed = 2.0 * (2.0*(speed-0.5))
speed = 2.0*(speed-0.5) # adjust to range -1 to +1
negative = speed<0.0 # remember negative state cos we'll lose it in next
# self.speed = 2.0 * (2.0*(speed-0.5))
speed = 2.0 * (speed - 0.5) # adjust to range -1 to +1
negative = speed < 0.0 # remember negative state cos we'll lose it in next
self.speed = (speed * speed) * 2.0
if negative: self.speed *= -1
print ("automation speed is now %s" % self.speed)
if negative:
self.speed *= -1
print("automation speed is now %s" % self.speed)
def toggle_automation(self):
if not self.is_playing():
@@ -81,26 +87,28 @@ class SequencePlugin(Plugin):
def toggle_pause_automation(self):
self.pause_flag = not self.is_paused()
self.last_delta = -1
#self.pause_flag = self.is_paused() and self.is_playing()
if not self.is_paused() and self.is_playing(): #not self.is_playing():
# self.pause_flag = self.is_paused() and self.is_playing()
if not self.is_paused() and self.is_playing(): # not self.is_playing():
self.run_automation()
last_delta = -1
last_delta = -1
def delta(self, now):
if self.last_delta==-1:
if self.last_delta == -1:
self.last_delta = now
r = now - self.last_delta
self.last_delta = now
return r
speed = 0.25 #1.0
speed = 0.25 # 1.0
def move_delta(self, delta, speed):
self.position += delta * speed
if self.position>1.0:
self.position = self.position-1.0
if self.position > 1.0:
self.position = self.position - 1.0
self.iterations_count += 1
elif self.position<0.0:
self.position = self.position+1.0
elif self.position < 0.0:
self.position = self.position + 1.0
self.iterations_count += 1
position = 0.0
@@ -111,12 +119,13 @@ class SequencePlugin(Plugin):
iterations_count = 0
duration = 2000
frequency = 100
def run_automation(self):
import time
now = time.time()
#print("running automation at %s!" % self.position)
# print("running automation at %s!" % self.position)
if not self.is_paused():
self.store_passed = None
delta = self.delta(now)
@@ -126,9 +135,9 @@ class SequencePlugin(Plugin):
if not self.stop_flag and not self.disabled:
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) )
# print("%s: stopping ! (stop_flag %s)" % ((now - self.automation_start),self.stop_flag) )
self.stop_flag = False
#self.automation_start = None
# self.automation_start = None
self.iterations_count = 0
def is_paused(self):
@@ -136,11 +145,12 @@ class SequencePlugin(Plugin):
def is_playing(self):
return not self.is_paused() or self.stop_flag
#return self.automation_start is not None
# 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)
@@ -148,7 +158,7 @@ class ActionsPlugin(Plugin):
@property
def parserlist(self):
return [
#( r"^test_plugin$", self.test_plugin )
# ( r"^test_plugin$", self.test_plugin )
]
# test if this plugin should handle the method name -- also covers if we're a DisplayPlugin
@@ -158,7 +168,7 @@ class ActionsPlugin(Plugin):
return True
for a in self.parserlist:
if (a[0]==method_name):
if (a[0] == method_name):
return True
regex = a[0]
me = a[1]
@@ -185,24 +195,28 @@ class ActionsPlugin(Plugin):
return (found_method, args)
class DisplayPlugin(Plugin):
"""Base class for plugins that want to show a user interface on the recur screen"""
def __init__(self, plugin_collection):
super().__init__(plugin_collection)
def is_handled(self, name):
raise NotImplementedError
raise NotImplementedError
def get_display_modes(self):
raise NotImplementedError
raise NotImplementedError
def show_plugin(self, display):
from tkinter import Text, END
#display_text.insert(END, 'test from DisplayPlugin')
from tkinter import END
# display_text.insert(END, 'test from DisplayPlugin')
display.display_text.insert(END, '{} \n'.format(display.body_title))
class ModulationReceiverPlugin(Plugin):
"""Base class for plugins that want to be notified of a change to modulation values"""
def __init__(self, plugin_collection):
super().__init__(plugin_collection)
@@ -210,8 +224,10 @@ class ModulationReceiverPlugin(Plugin):
print("||||||set_modulation_value dummy!")
raise NotImplementedError
class AutomationSourcePlugin(Plugin):
"""Base class for plugins that offer things to save&playback to&from automation"""
@property
def frame_key(self):
return self.__class__.__name__
@@ -226,7 +242,7 @@ class AutomationSourcePlugin(Plugin):
raise NotImplementedError
def get_frame_summary(self, data):
line = self.__class__.name + "%s: %s"%(self.__class__.name,data)
line = self.__class__.name + "%s: %s" % (self.__class__.name, data)
return line
# these frame stubs deal with the simplest case of a frame being a dict of values
@@ -243,36 +259,36 @@ class AutomationSourcePlugin(Plugin):
return cf.copy()
diff = {}
for queue,message in cf.items():
if lf.get(queue) is None or lf.get(queue)!=message:
for queue, message in cf.items():
if lf.get(queue) is None or lf.get(queue) != message:
diff[queue] = message
#print (">>>>>> returning diff\n%s\n<<<<<" % diff)
# print (">>>>>> returning diff\n%s\n<<<<<" % diff)
return diff
def merge_data(self, data1, data2):
#print (">>>merge_data passed\n\t%s\nand\n\t%s" % (data1,data2))
# print (">>>merge_data passed\n\t%s\nand\n\t%s" % (data1,data2))
output = {}
if data1 is None:
output = data2.copy()
else:
output = data1.copy()
output.update(data2)
#print("merge_data returning\n\t%s" % output)
#print("<<<<<<")
# print("merge_data returning\n\t%s" % output)
# print("<<<<<<")
return output
def get_ignored_data(self, data, ignored):
#frame = self.f
f = data.copy() #frame.get(self.frame_key,{})
for queue,item in f.items(): #frame.get(self.frame_key,{}).items():
# frame = self.f
f = data.copy() # frame.get(self.frame_key,{})
for queue, item in f.items(): # frame.get(self.frame_key,{}).items():
if ignored.get(queue) is not None:
#print ("\tfound that should ignore %s (%s) ?" % (queue, item))
# print ("\tfound that should ignore %s (%s) ?" % (queue, item))
f[queue] = None
return f
def is_frame_data_empty(self, data):
if len(data)>0:
if len(data) > 0:
return False
return True
@@ -281,6 +297,7 @@ class AutomationSourcePlugin(Plugin):
### TODO: experimental value interpolation -- doesn't work, and is slow!
cmd_size = {}
def process_interpolate_clip(self, frames):
# loop over every frame
# for each property of each frame
@@ -290,9 +307,9 @@ class AutomationSourcePlugin(Plugin):
# else,
# store as last value
print("AutomationSourcePlugin[%s] got pre-interpolated clip: %s" % ( type(self), [ f.f for f in frames if f is not None]) )
print("AutomationSourcePlugin[%s] got pre-interpolated clip: %s" % (type(self), [f.f for f in frames if f is not None]))
#last = [ [None]*4, [None]*4, [None]*4 ]
# last = [ [None]*4, [None]*4, [None]*4 ]
last = {}
"""for findex,frame in enumerate(frames):
@@ -300,17 +317,17 @@ class AutomationSourcePlugin(Plugin):
continue"""
reproc_to = 0
queues = []# queue for queue in list(frame.f.get(self.frame_key,{}).keys()) for frame in frames ] # get all queues in all frames in clip
queues = [] # queue for queue in list(frame.f.get(self.frame_key,{}).keys()) for frame in frames ] # get all queues in all frames in clip
for frame in frames:
if frame is not None:
for queue,command in frame.f.get(self.frame_key,{}).items():
for queue, command in frame.f.get(self.frame_key, {}).items():
queues.append(queue)
if command is not None and len(command)==2:
if command is not None and len(command) == 2:
self.cmd_size[queue] = len(command[1])
if command is not None and command[1] is not None:
last[queue] = command
queues = list(set(queues))
print ("got queues %s" % queues)
print("got queues %s" % queues)
"""distance_cache = [{}]*len(frames) # list [ dict { queue: list [ distance to next arg args ] } ]
bob = {}
@@ -322,88 +339,88 @@ class AutomationSourcePlugin(Plugin):
distance_cache[i-1] = bob"""
def process(self, findex, frame):
#for queue,command in enumerate(frame.f.get(self.frame_key,[])):
data = frame.f.get(self.frame_key,None)
#if data is None:
# for queue,command in enumerate(frame.f.get(self.frame_key,[])):
data = frame.f.get(self.frame_key, None)
# if data is None:
# return
for queue in queues:
if last.get(queue) is not None:
"""if data.get(queue) is not None:
last[queue] = data.get(queue)
continue"""
for argindex,value in enumerate(last.get(queue)[1]):
#print ("findex %s: for argindex %s got last value %s" % (findex, argindex, value))
#if data is not None: print ("data queue is %s" % data.get(queue,None))
if data is not None and data.get(queue,None) is not None and len(data.get(queue))>0 and len(data.get(queue)[1])>argindex and data.get(queue)[1][argindex] is not None:
last[queue][1][argindex] = data.get(queue)[1][argindex]
continue
gap,future_value = self.get_distance_value_command(frames,findex,queue,argindex)
if gap==0 or future_value==value:
continue
#print("\tpassing %s and %s to interpolate" % (last[queue][argindex], future_value))
newvalue = self.pc.fm.interpolate(last[queue][1][argindex], future_value, gap)
if data is None:
frame.f[self.frame_key] = {}
data = frame.f[self.frame_key]
if data.get(queue) is None:
data[queue] = [last[queue][0], last[queue][1]]# [None]*self.cmd_size[queue]]
#while len(data.get(queue)[1])<argindex:
# data.get(queue)[1] += [] #.append(None)
data.get(queue)[1][argindex] = int(newvalue)
last[queue][1][argindex] = int(newvalue)
"""if data.get(queue) is not None:
last[queue] = data.get(queue)
continue"""
for argindex, value in enumerate(last.get(queue)[1]):
# print ("findex %s: for argindex %s got last value %s" % (findex, argindex, value))
# if data is not None: print ("data queue is %s" % data.get(queue,None))
if data is not None and data.get(queue, None) is not None and len(data.get(queue)) > 0 and len(data.get(queue)[1]) > argindex and data.get(queue)[1][argindex] is not None:
last[queue][1][argindex] = data.get(queue)[1][argindex]
continue
gap, future_value = self.get_distance_value_command(frames, findex, queue, argindex)
if gap == 0 or future_value == value:
continue
# print("\tpassing %s and %s to interpolate" % (last[queue][argindex], future_value))
newvalue = self.pc.fm.interpolate(last[queue][1][argindex], future_value, gap)
if data is None:
frame.f[self.frame_key] = {}
data = frame.f[self.frame_key]
if data.get(queue) is None:
data[queue] = [last[queue][0], last[queue][1]] # [None]*self.cmd_size[queue]]
# while len(data.get(queue)[1])<argindex:
# data.get(queue)[1] += [] #.append(None)
data.get(queue)[1][argindex] = int(newvalue)
last[queue][1][argindex] = int(newvalue)
elif data is not None and data.get(queue) is not None:
#print("no last[%s] already set, setting to %s" % (queue, data.get(queue)))
# print("no last[%s] already set, setting to %s" % (queue, data.get(queue)))
last[queue] = data.get(queue)
for i in range(1):
for findex,frame in enumerate(frames):
if frame is None:
continue
for findex, frame in enumerate(frames):
if frame is None:
continue
process(self,findex,frame)
process(self, findex, frame)
print("\nAutomationSourcePlugin got interpolated clip: %s" % [ f.f for f in frames if f is not None ])
print("\nAutomationSourcePlugin got interpolated clip: %s" % [f.f for f in frames if f is not None])
self.distance_cache = {}
distance_cache = {}
def get_distance_value_command(self, frames, findex, queue, argindex):
distance_cache = self.distance_cache
# check if we have a cached value that is lower than the findex
if distance_cache.get(queue) is not None and len(distance_cache.get(queue))>=(argindex+1) and distance_cache.get(queue)[argindex] is not None and distance_cache.get(queue)[argindex]['position'] >= findex:
if distance_cache.get(queue) is not None and len(distance_cache.get(queue)) >= (argindex + 1) and distance_cache.get(queue)[argindex] is not None and distance_cache.get(queue)[argindex]['position'] >= findex:
position = distance_cache.get(queue)[argindex]['position']
#return len(frames)-
return abs(position-findex), distance_cache.get(queue)[argindex]['value']
# return len(frames)-
return abs(position - findex), distance_cache.get(queue)[argindex]['value']
#print("\t\tget_distance_value_command(findex %s, queue %s, argindex %s)" %(findex,queue,argindex))
for i in range(1,len(frames)):
# print("\t\tget_distance_value_command(findex %s, queue %s, argindex %s)" %(findex,queue,argindex))
for i in range(1, len(frames)):
search_findex = i + findex
search_findex %= len(frames)
if frames[search_findex] is None:
continue
#print("\t\t\tgetting frame index %s" % search_findex)
# print("\t\t\tgetting frame index %s" % search_findex)
frame = frames[search_findex]
data = frame.f.get(self.frame_key,None)
#print("\t\t\tgot frame data %s" % data)
data = frame.f.get(self.frame_key, None)
# print("\t\t\tgot frame data %s" % data)
if data is None:
continue
command = data.get(queue,None)
command = data.get(queue, None)
if command is None:
continue
print("\t\t\tget_distance_value_command testing %s argindex %s - command looks like %s" % (queue, argindex, command))
if len(command[1])>argindex:
if command[1][argindex] is not None and findex!=i:
if len(command[1]) > argindex:
if command[1][argindex] is not None and findex != i:
print("\t\t\t\t\tgot distance %s to value for argindex %s: %s" % (i, argindex, command[1][argindex]))
if distance_cache.get(queue) is None:
distance_cache[queue] = [None]*(self.cmd_size[queue])
#while len(distance_cache[queue])<(argindex+1):
distance_cache[queue] = [None] * (self.cmd_size[queue])
# while len(distance_cache[queue])<(argindex+1):
# distance_cache[queue] += [None]
distance_cache[queue][argindex] = { 'position': i, 'value': command[1][argindex] }
distance_cache[queue][argindex] = {'position': i, 'value': command[1][argindex]}
return i, command[1][argindex]
"""if frames[search_findex] is not None and frames[search_findex].f.get(self.frame_key,{}).get(queue,[])[argindex] is not None:
return i, frames[search_findex].f.get(self.frame_key,{}).get(queue,[])[argindex]"""
return 0, None
@@ -416,6 +433,7 @@ 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 display(self):
return self.actions.display
@@ -438,9 +456,9 @@ class PluginCollection(object):
"""
self.plugin_package = plugin_package
self.message_handler = message_handler
#self.shaders = lambda: data.shaders
# self.shaders = lambda: data.shaders
self.data = data
#self.actions = message_handler.actions
# self.actions = message_handler.actions
self.reload_plugins()
# set up a FrameManager too so that plugins can use it
@@ -448,6 +466,7 @@ class PluginCollection(object):
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)
@@ -464,7 +483,8 @@ class PluginCollection(object):
def quit_plugins(self):
# tell each plugin to quit
for plugin in self.get_plugins():
if not plugin.disabled: plugin.stop_plugin()
if not plugin.disabled:
plugin.stop_plugin()
def stop_plugin_name(self, name):
for plugin in self.get_plugins(include_disabled=True):
@@ -472,15 +492,14 @@ class PluginCollection(object):
plugin.stop_plugin()
def start_plugin_name(self, name):
#print("start_plugin_name got %s"%name)
# print("start_plugin_name got %s"%name)
for plugin in self.get_plugins(include_disabled=True):
#print("looking for %s vs %s" % (type(plugin).__name__, name))
# print("looking for %s vs %s" % (type(plugin).__name__, name))
if type(plugin).__name__ == name:
#print("starting %s" %name)
# print("starting %s" %name)
plugin.start_plugin()
def get_plugins(self, clazz = None, include_disabled = False):
def get_plugins(self, clazz=None, include_disabled=False):
if clazz:
return [c for c in self.plugins if isinstance(c, clazz) and (include_disabled or not c.disabled)]
else:
@@ -488,8 +507,8 @@ class PluginCollection(object):
def get_plugin_for_class_name(self, class_name):
for plugin in self.get_plugins():
#print("got class name %s" % type(plugin).__name__==class_name)
if type(plugin).__name__==class_name == class_name:
# print("got class name %s" % type(plugin).__name__==class_name)
if type(plugin).__name__ == class_name == class_name:
return plugin
return None
@@ -505,12 +524,11 @@ class PluginCollection(object):
for (_, c) in clsmembers:
# Only add classes that are a sub class of Plugin, but NOT Plugin itself
# or one of the base classes defined in this file
ignore_list = [ Plugin, ActionsPlugin, SequencePlugin, MidiFeedbackPlugin, DisplayPlugin, ModulationReceiverPlugin, AutomationSourcePlugin ]
ignore_list = [Plugin, ActionsPlugin, SequencePlugin, MidiFeedbackPlugin, DisplayPlugin, ModulationReceiverPlugin, AutomationSourcePlugin]
if issubclass(c, Plugin) & (c not in ignore_list):
print(' Found plugin class: %s.%s' % (c.__module__,c.__name__))
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
# disabled 03-2020 to try and avoid problem with subclasses-of-subclasses being listed twice..
@@ -532,7 +550,6 @@ class PluginCollection(object):
for child_pkg in child_pkgs:
self.walk_package(package + '.' + child_pkg)"""
## helpers
def get_variable(self, varname, default=0.0):
from plugins.ManipulatePlugin import ManipulatePlugin

View File

@@ -1,13 +1,15 @@
from tkinter import Text, END
import math
import time
from tkinter import END, Text
import display_centre.menu as menu
class Display(object):
MENU_HEIGHT = 10
SELECTOR_WIDTH = 0.47
ROW_OFFSET = 6.0
TITLES = ['{0} r_e_c_u_r {0}'.format('='*18), '{0} c_o_n_j_u_r {1}'.format('='*18, '='*16), '{0} d_e_t_o_u_r {1}'.format('='*18, '='*16)]
TITLES = ['{0} r_e_c_u_r {0}'.format('=' * 18), '{0} c_o_n_j_u_r {1}'.format('=' * 18, '=' * 16), '{0} d_e_t_o_u_r {1}'.format('=' * 18, '=' * 16)]
def __init__(self, tk, video_driver, shaders, message_handler, data):
self.tk = tk
@@ -16,7 +18,7 @@ class Display(object):
self.shaders = shaders
self.message_handler = message_handler
self.data = data
self.browser_menu = menu.BrowserMenu(self.data, self.message_handler, self.MENU_HEIGHT)
self.browser_menu = menu.BrowserMenu(self.data, self.message_handler, self.MENU_HEIGHT)
self.settings_menu = menu.SettingsMenu(self.data, self.message_handler, self.MENU_HEIGHT)
self.plugins_menu = menu.PluginsMenu(self.data, self.message_handler, self.MENU_HEIGHT)
self.shaders_menu = self.shaders.shaders_menu
@@ -43,13 +45,13 @@ class Display(object):
self.display_text.tag_configure("FUNCTION", background="yellow", foreground="black")
self.display_text.tag_configure("BROKEN_PATH", background="black", foreground="gray")
self.display_text.tag_configure("ZEBRA_STRIPE", background="black", foreground="khaki")
def _load_display(self):
self._load_title()
self._load_player()
self._load_display_body()
self._load_message()
#print('the number of tags are {}'.format(len(self.display_text.tag_names())))
# print('the number of tags are {}'.format(len(self.display_text.tag_names())))
self.display_text.pack()
def _load_title(self):
@@ -77,7 +79,7 @@ class Display(object):
self.display_text.insert(END, status + '\n')
self.display_text.tag_add("NOW_ALPHA", 3.0, 3.17)
self.display_text.tag_add("CAPTURE_ALPHA", 3.18, 3.29)
self.display_text.tag_add("NEXT_ALPHA", 3.29, 3.47)
self.display_text.tag_add("NEXT_ALPHA", 3.29, 3.47)
def _load_display_body(self):
self.body_title = self._generate_body_title()
@@ -104,35 +106,34 @@ class Display(object):
self._load_plugin_page(self.data.display_mode, plugin)
self.display_text.tag_add("DISPLAY_MODE", 4.19, 4.29)
self.display_text.tag_add("COLUMN_NAME", 5.0, 6.0)
def _load_plugin_page(self, display_mode, plugin):
plugin.show_plugin(self, display_mode)
def _load_sampler(self):
bank_data = self.data.bank_data[self.data.bank_number]
self.display_text.insert(END, '{} \n'.format(self.body_title))
self.display_text.insert(END, '{:>6} {:<17} {:>5} {:<5} {:<5} \n'.format(
'{}-slot'.format(self.data.bank_number), 'name', 'length', 'start', 'end'))
'{}-slot'.format(self.data.bank_number), 'name', 'length', 'start', 'end'))
for index, slot in enumerate(bank_data):
name_without_extension = slot['name'].rsplit('.',1)[0]
name_without_extension = slot['name'].rsplit('.', 1)[0]
self.display_text.insert(END, '{:^6} {:<17} {:^5} {:>5} {:<5} \n'.format(
index, name_without_extension[0:17], self.format_time_value(slot['length']),
self.format_time_value(slot['start']), self.format_time_value(slot['end'])))
index, name_without_extension[0:17], self.format_time_value(slot['length']),
self.format_time_value(slot['start']), self.format_time_value(slot['end'])))
if index % 2:
self.display_text.tag_add("ZEBRA_STRIPE", self.ROW_OFFSET + index,
self.ROW_OFFSET + self.SELECTOR_WIDTH + index)
self.ROW_OFFSET + self.SELECTOR_WIDTH + index)
if self.data.is_this_path_broken(slot['location']):
self.display_text.tag_add("BROKEN_PATH", self.ROW_OFFSET + index,
self.ROW_OFFSET + self.SELECTOR_WIDTH + index)
self.ROW_OFFSET + self.SELECTOR_WIDTH + index)
# highlight the slot of the selected player
if self.data.player_mode == 'next':
bank_slot = self.video_driver.next_player.bankslot_number
bank_slot = self.video_driver.next_player.bankslot_number
else:
bank_slot = self.video_driver.current_player.bankslot_number
current_bank , current_slot = self.data.split_bankslot_number(bank_slot)
bank_slot = self.video_driver.current_player.bankslot_number
current_bank, current_slot = self.data.split_bankslot_number(bank_slot)
if current_bank is self.data.bank_number:
self._highlight_this_row(current_slot)
@@ -140,7 +141,7 @@ class Display(object):
browser_list = self.browser_menu.menu_list
number_of_lines_displayed = 0
self.display_text.insert(END, '{} \n'.format(self.body_title))
self.display_text.insert(END, '{:40} {:5} \n'.format('path', 'slot'))
number_of_browser_items = len(browser_list)
@@ -161,7 +162,7 @@ class Display(object):
line_count = 0
settings_list = self.settings_menu.menu_list
self.display_text.insert(END, '{} \n'.format(self.body_title))
self.display_text.insert(END, '{:^23} {:^22} \n'.format('SETTING', 'VALUE'))
number_of_settings_items = len(settings_list)
for index in range(number_of_settings_items):
@@ -180,17 +181,17 @@ class Display(object):
def _load_plugins(self):
line_count = 0
self.display_text.insert(END, '{} \n'.format(self.body_title))
self.display_text.insert(END, '{:<35} {:<8} \n'.format('plugin', 'status'))
self.display_text.insert(END, '{:<35} {:<8} \n'.format('plugin', 'status'))
## showing list of plugins:
plugins_list = sorted([
(type(plugin).__name__, type(plugin).__name__ in self.data.get_enabled_plugin_class_names())\
for plugin in self.data.plugins.get_plugins(include_disabled=True)
plugins_list = sorted([
(type(plugin).__name__, type(plugin).__name__ in self.data.get_enabled_plugin_class_names()) \
for plugin in self.data.plugins.get_plugins(include_disabled=True)
])
self.plugins_menu.menu_list = plugins_list
number_of_plugins = len(plugins_list)
for index in range(number_of_plugins):
if line_count >= self.MENU_HEIGHT :
if line_count >= self.MENU_HEIGHT:
break
if index >= self.plugins_menu.top_menu_index:
plugin_line = plugins_list[index]
@@ -198,34 +199,33 @@ class Display(object):
line_count = line_count + 1
for index in range(self.plugins_menu.top_menu_index + self.plugins_menu.menu_height - number_of_plugins):
self.display_text.insert(END, '\n')
self.display_text.insert(END, '\n')
self._highlight_this_row(self.plugins_menu.selected_list_index - self.plugins_menu.top_menu_index)
def _load_shaders(self):
line_count = 0
self.display_text.insert(END, '{} \n'.format(self.body_title))
## showing current shader info:
shader = self.shaders.selected_shader_list[self.data.shader_layer]
self.display_text.insert(END, '{:<1}{}{:<1}:{:<2} {:<16} '.format \
(self.data.shader_layer,
self.get_speed_indicator(self.shaders.selected_speed_list[self.data.shader_layer]),
self.shaders.selected_status_list[self.data.shader_layer],shader['shad_type'][0],
shader['name'].lstrip()[0:16] ))
for i in range(min(4,shader['param_number'])):
self.get_speed_indicator(self.shaders.selected_speed_list[self.data.shader_layer]),
self.shaders.selected_status_list[self.data.shader_layer], shader['shad_type'][0],
shader['name'].lstrip()[0:16]))
for i in range(min(4, shader['param_number'])):
display_param = self.format_param_value(self.shaders.selected_param_list[self.data.shader_layer][i])
if display_param == 100:
display_param == 99
self.display_text.insert(END, 'x{}:{:02d}'.format(i, display_param))
self.display_text.insert(END,'\n')
self.display_text.insert(END, '\n')
self.display_text.tag_add("COLUMN_NAME", 5.0, 6.0)
## showing list of other shaders:
shaders_list = self.shaders.shaders_menu_list
number_of_shader_items = len(shaders_list)
for index in range(number_of_shader_items):
if line_count >= self.MENU_HEIGHT :
if line_count >= self.MENU_HEIGHT:
break
if index >= self.shaders.shaders_menu.top_menu_index:
shader_line = shaders_list[index]
@@ -233,7 +233,7 @@ class Display(object):
line_count = line_count + 1
for index in range(self.shaders.shaders_menu.top_menu_index + self.shaders.shaders_menu.menu_height - number_of_shader_items):
self.display_text.insert(END, '\n')
self.display_text.insert(END, '\n')
self._highlight_this_row(self.shaders.shaders_menu.selected_list_index - self.shaders.shaders_menu.top_menu_index)
if self.data.control_mode == "SHADER_PARAM":
@@ -241,16 +241,16 @@ class Display(object):
def _load_shader_bank(self):
shader_bank_data = self.data.shader_bank_data[self.data.shader_layer]
self.display_text.insert(END, '{} \n'.format(self.body_title))
self.display_text.insert(END, '{:>6} {:<11} {:<5} '.format(
'{} {}'.format(self.data.shader_layer, self.get_speed_indicator(self.shaders.selected_speed_list[self.data.shader_layer])),
'name', 'type'))
'{} {}'.format(self.data.shader_layer, self.get_speed_indicator(self.shaders.selected_speed_list[self.data.shader_layer])),
'name', 'type'))
shader = self.shaders.selected_shader_list[self.data.shader_layer]
for i in range(min(4,shader['param_number'])):
for i in range(min(4, shader['param_number'])):
display_param = self.format_param_value(self.shaders.selected_param_list[self.data.shader_layer][i])
if display_param == 100:
display_param == 99
@@ -258,11 +258,11 @@ class Display(object):
self.display_text.insert(END, '\n')
for index, slot in enumerate(shader_bank_data):
name_without_extension = slot['name'].rsplit('.',1)[0]
name_without_extension = slot['name'].rsplit('.', 1)[0]
self.display_text.insert(END, '{:^6} {:<17} {:<5} \n'.format(index, name_without_extension[0:17], slot['shad_type']))
if index % 2:
self.display_text.tag_add("ZEBRA_STRIPE", self.ROW_OFFSET + index,
self.ROW_OFFSET + self.SELECTOR_WIDTH + index)
self.ROW_OFFSET + self.SELECTOR_WIDTH + index)
# highlight the slot of the selected player
current_slot = self.shaders.selected_shader_list[self.data.shader_layer].get('slot', None)
not_playing_tag = self.shaders.selected_status_list[self.data.shader_layer] != ''
@@ -273,15 +273,15 @@ class Display(object):
def _load_modulation_bank(self):
shader_bank_data = self.data.shader_bank_data[self.data.shader_layer]
self.display_text.insert(END, '{} \n'.format(self.body_title))
self.display_text.insert(END, '{:>6} {:<11} {:<5} '.format(
'{} {}'.format(self.data.shader_layer, self.get_speed_indicator(self.shaders.selected_speed_list[self.data.shader_layer])),
'name', 'type'))
'{} {}'.format(self.data.shader_layer, self.get_speed_indicator(self.shaders.selected_speed_list[self.data.shader_layer])),
'name', 'type'))
shader = self.shaders.selected_shader_list[self.data.shader_layer]
"""for i in range(min(4,shader['param_number'])):
display_param = self.format_param_value(self.shaders.selected_param_list[self.data.shader_layer][i])
if display_param == 100:
@@ -312,8 +312,8 @@ class Display(object):
self._highlight_this_param(self.shaders.focused_param) """
# show info about the modulation configuration
#self.display_text.insert(END, "Lyr|1a b c d|2a b c d|3a b c d|4a b c d\n")
#self.display_text.insert(END, "Lyr")
# self.display_text.insert(END, "Lyr|1a b c d|2a b c d|3a b c d|4a b c d\n")
# self.display_text.insert(END, "Lyr")
"""for i in range(4):
self.display_text.insert(END, "|%s"%i)
for i in range(4):
@@ -321,7 +321,7 @@ class Display(object):
if i==self.shaders.selected_modulation_slot:
a = a.upper()
self.display_text.insert(END, "%s "%a)"""
#self.display_text.insert(END, "\n")
# self.display_text.insert(END, "\n")
"""for layer, modulations in enumerate(self.shaders.modulation_level):
if (layer==self.data.shader_layer):
self.display_text.insert(END, '*')
@@ -341,14 +341,14 @@ class Display(object):
o += "\n Modmatrix:\t"
name = self.shaders.selected_shader_list[layer].get('name').strip()
#o = ""
# o = ""
for slot in range(4):
sl = self.get_mod_slot_label(slot)
o+= sl + ("[" if sl.isupper() else "-")
o += sl + ("[" if sl.isupper() else "-")
for param in range(4):
o += self.get_bar(self.shaders.modulation_level[layer][param][slot])
o+= ("]" if sl.isupper() else "-") + " "
self.display_text.insert(END, "%s %s:\t%s\n\n"%(">" if layer==self.data.shader_layer else " ", layer, o))
o += ("]" if sl.isupper() else "-") + " "
self.display_text.insert(END, "%s %s:\t%s\n\n" % (">" if layer == self.data.shader_layer else " ", layer, o))
self.display_text.insert(END, '\n')
# todo: this doesnt work but would be a better way to highlight the selected modulation slot/layer
"""self._highlight_this_param(
@@ -358,12 +358,11 @@ class Display(object):
column_offset = 0.1
)"""
def _load_detour(self):
line_count = 0
self.display_text.insert(END, '{} \n'.format(self.body_title))
## showing current detour info:
## showing current detour info:
self.display_text.insert(END, '{:^23} {:^22} \n'.format('SETTING', 'VALUE'))
self.display_text.insert(END, '{:>23} {:<22} \n'.format("DETOUR_ACTIVE", self.data.detour_active))
for index, (key, value) in enumerate(self.data.detour_settings.items()):
@@ -377,30 +376,30 @@ class Display(object):
def _load_message(self):
if self.message_handler.current_message[1]:
self.display_text.insert(END, '{:5} {:42} \n'.format(
self.message_handler.current_message[0], self.message_handler.current_message[1][0:38]))
self.message_handler.current_message[0], self.message_handler.current_message[1][0:38]))
self.display_text.tag_add('{}_MESSAGE'.format(
self.message_handler.current_message[0]), 16.0,16.0 + self.SELECTOR_WIDTH)
self.message_handler.current_message[0]), 16.0, 16.0 + self.SELECTOR_WIDTH)
if self.message_handler.current_message[2]:
self.message_handler.current_message[2] = False
message_length = 4000
self.tk.after(message_length, self.message_handler.clear_message)
elif self.data.function_on:
self.display_text.insert(END, '{:^47} \n'.format('< FUNCTION KEY ON >'))
self.display_text.tag_add('FUNCTION', 16.0,16.0 + self.SELECTOR_WIDTH)
self.display_text.tag_add('FUNCTION', 16.0, 16.0 + self.SELECTOR_WIDTH)
else:
feedback = ''
if self.data.feedback_active:
feedback = 'FDBCK'
self.display_text.insert(END, '{:8} {:<28} {:>5} \n'.format('CONTROL:', str(self.data.control_mode), feedback))
self.display_text.tag_add('TITLE', 16.0,16.0 + self.SELECTOR_WIDTH)
self.display_text.tag_add('TITLE', 16.0, 16.0 + self.SELECTOR_WIDTH)
def _highlight_this_row(self, row, gray=False):
highlight_tag = "SELECT"
if gray:
highlight_tag = "BROKEN_PATH"
highlight_tag = "BROKEN_PATH"
self.display_text.tag_remove("ZEBRA_STRIPE", self.ROW_OFFSET + row,
self.ROW_OFFSET + self.SELECTOR_WIDTH + row)
self.ROW_OFFSET + self.SELECTOR_WIDTH + row)
self.display_text.tag_add(highlight_tag, self.ROW_OFFSET + row,
self.ROW_OFFSET + self.SELECTOR_WIDTH + row)
@@ -408,13 +407,13 @@ class Display(object):
self.display_text.tag_remove("SELECT", self.ROW_OFFSET + row,
self.ROW_OFFSET + self.SELECTOR_WIDTH + row)
def _highlight_this_param(self, param_num, column_offset = 0.26, param_length = 0.05, param_row = None):
def _highlight_this_param(self, param_num, column_offset=0.26, param_length=0.05, param_row=None):
if param_row is None:
param_row = self.ROW_OFFSET - 1
self.display_text.tag_add("SHADER_PARAM",
round(param_row + column_offset + param_num*param_length,2),
round(param_row + column_offset + (param_num+1)*param_length, 2)
)
self.display_text.tag_add("SHADER_PARAM",
round(param_row + column_offset + param_num * param_length, 2),
round(param_row + column_offset + (param_num + 1) * param_length, 2)
)
def _get_status_for_player(self):
now_slot, now_status, now_alpha, next_slot, next_status, next_alpha = self.video_driver.get_player_info_for_status()
@@ -425,20 +424,19 @@ class Display(object):
else:
capture_status = ''
preview_alpha = 0
if preview_alpha == None:
preview_alpha = 0
#print('capture alpha is {}'.format(preview_alpha))
# print('capture alpha is {}'.format(preview_alpha))
self._set_colour_from_alpha(now_alpha, preview_alpha, next_alpha)
self._set_colour_from_alpha(now_alpha, preview_alpha, next_alpha)
now_info = 'NOW [{}] {}'.format(now_slot, now_status)
next_info = 'NEXT [{}] {}'.format(next_slot, next_status)
capture_info = '{}'.format(capture_status)
return '{:17} {:10} {:17}'.format(now_info[:17], capture_info[:10], next_info[:18])
return '{:17} {:10} {:17}'.format(now_info[:17], capture_info[:10], next_info[:18])
def _get_banner_for_player(self,player):
def _get_banner_for_player(self, player):
start, end, position = self.video_driver.get_player_info_for_banner(player)
banner = self.create_video_display_banner(start, end, position)
time_been = self.format_time_value(position - start)
@@ -446,35 +444,37 @@ class Display(object):
return ' {:5} {} {:5}'.format(time_been, banner, time_left)
def _generate_capture_status(self):
is_previewing = self.capture.is_previewing
is_previewing = self.capture.is_previewing
is_recording = self.capture.is_recording
rec_time = -1
if is_recording == True:
rec_time = self.capture.get_recording_time()
capture_status = ''
if is_previewing and is_recording == True:
capture_status = '<{}>'.format('REC'+ self.format_time_value(rec_time))
capture_status = '<{}>'.format('REC' + self.format_time_value(rec_time))
elif is_previewing and is_recording == 'saving':
capture_status = '<{}>'.format('_saving_')
elif is_previewing:
capture_status = '<{}>'.format('_preview')
elif is_recording == True:
capture_status = '[{}]'.format('REC'+ self.format_time_value(rec_time))
capture_status = '[{}]'.format('REC' + self.format_time_value(rec_time))
elif is_recording == 'saving':
capture_status = '[{}]'.format('_saving_')
capture_status = '[{}]'.format('_saving_')
else:
capture_status = ''
return capture_status
def get_bar(self, value, max_value = 1.0):
def get_bar(self, value, max_value=1.0):
if value is None:
return " "
value = abs(value / max_value) # abs() so negative values make some sense
if value>1.0: value = 1.0
elif value<0.0: value = 0.0
value = abs(value / max_value) # abs() so negative values make some sense
if value > 1.0:
value = 1.0
elif value < 0.0:
value = 0.0
bar = u"_\u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588"
g = '%s'%bar[int(value*(len(bar)-1))]
g = '%s' % bar[int(value * (len(bar) - 1))]
return g
def get_mod_slot_label(self, slot):
@@ -483,35 +483,35 @@ class Display(object):
sl = sl.lower()
return sl
def get_speed_indicator(self, value, convert = True):
def get_speed_indicator(self, value, convert=True):
if convert:
value = (value * 2.0) - 1.0 # convert 0 to 1 to -1 to +1
value = (value * 2.0) - 1.0 # convert 0 to 1 to -1 to +1
output = u""
if value==0.0 or (value>=-0.02 and value<=0.02):
output+=u"\u23f9" # stopped
elif value<=-0.5:
output+=u"\u00AB" # fast reverse
elif value<0.0:
output+=u"\u2039" # reverse
elif value>=0.5:
output+=u"\u00BB" # fast forward
elif value>0.0:
output+=u"\u203A" # forward
if value == 0.0 or (value >= -0.02 and value <= 0.02):
output += u"\u23f9" # stopped
elif value <= -0.5:
output += u"\u00AB" # fast reverse
elif value < 0.0:
output += u"\u2039" # reverse
elif value >= 0.5:
output += u"\u00BB" # fast forward
elif value > 0.0:
output += u"\u203A" # forward
#output += " {:03f}".format(value)
# output += " {:03f}".format(value)
output += self.get_bar(value)
return output
def get_compact_indicators(self, inp):
step = 2
step = 2
s = ""
for i in range(0,len(inp),step): # number of shader slots per layer
for i in range(0, len(inp), step): # number of shader slots per layer
selected1 = inp[i]
if i+1 > len(inp): # catch if odd number of elements passed to us?
if i + 1 > len(inp): # catch if odd number of elements passed to us?
selected2 = False
else:
selected2 = inp[i+1]
selected2 = inp[i + 1]
if selected1 and selected2:
# full block
@@ -526,11 +526,9 @@ class Display(object):
# empty
s += "_"
#s += "#" if selected else "-"
# s += "#" if selected else "-"
return s
@staticmethod
def create_video_display_banner(start, end, position):
banner_list = ['[', '-', '-', '-', '-', '-', '-', '-', '-',
@@ -543,8 +541,8 @@ class Display(object):
banner_list[0] = '<'
elif position > end:
banner_list[max] = '>'
elif end - start != 0 and not math.isnan(position) :
#print('start value is {}, end value is {}, position is {}'.format(start, end, position))
elif end - start != 0 and not math.isnan(position):
# print('start value is {}, end value is {}, position is {}'.format(start, end, position))
marker = int(math.floor(float(position - start) /
float(end - start) * (max - 1)) + 1)
banner_list[marker] = '*'
@@ -562,18 +560,18 @@ class Display(object):
max = len(banner_list) - 1
if size == 0:
size = max
#print('start value is {}, end value is {}, position is {}'.format(start, end, position))
# print('start value is {}, end value is {}, position is {}'.format(start, end, position))
if start > 0:
start = int(math.floor(float(start) /
float(size) * (max - 1)) + 1)
float(size) * (max - 1)) + 1)
banner_list[start] = '['
if end > 0:
end = int(math.floor(float(end) /
float(size) * (max - 1)) + 1)
float(size) * (max - 1)) + 1)
banner_list[end] = ']'
position = int(math.floor(float(position) /
float(size) * (max - 1)) + 1)
if 0 <= position and position < len(banner_list):
float(size) * (max - 1)) + 1)
if 0 <= position and position < len(banner_list):
banner_list[position] = '*'
return ''.join(banner_list)
@@ -589,43 +587,43 @@ class Display(object):
else:
is_recording = False
### scale values
scaled_now = int(( now_alpha / 255 ) * (255 - upper_bound) + upper_bound)
scaled_preview = int(( preview_alpha / 255 ) * (255 - upper_bound) + upper_bound)
scaled_next = int(( next_alpha / 255 ) * (255 - upper_bound) + upper_bound)
scaled_now = int((now_alpha / 255) * (255 - upper_bound) + upper_bound)
scaled_preview = int((preview_alpha / 255) * (255 - upper_bound) + upper_bound)
scaled_next = int((next_alpha / 255) * (255 - upper_bound) + upper_bound)
### convert to hex
now_colour = self.hex_from_rgb(scaled_now, scaled_now, 0)
capture_colour = self.hex_from_rgb(255 * is_recording, 100, scaled_preview )
capture_colour = self.hex_from_rgb(255 * is_recording, 100, scaled_preview)
next_colour = self.hex_from_rgb(0, scaled_next, scaled_next)
### update the colours
self.display_text.tag_configure("NOW_ALPHA", background="black", foreground=now_colour)
self.display_text.tag_configure("CAPTURE_ALPHA", background="black", foreground=capture_colour)
self.display_text.tag_configure("NEXT_ALPHA", background="black", foreground=next_colour)
self.display_text.tag_configure("NEXT_ALPHA", background="black", foreground=next_colour)
def _generate_body_title(self):
display_modes = self.data.get_display_modes_list()
current_mode = self.data.display_mode
selected_list = []
for index, v in enumerate(display_modes):
if v == current_mode:
if v == current_mode:
while len(v) < 8:
v = v + '_'
selected_list.append('[{}]'.format(v))
selected_list_index = index
else:
selected_list.append('<{}>'.format(v[:2].lower()))
# 18 char to PURPLE : 18 - 29 ,18 after
if selected_list_index>4:
selected_list = selected_list[selected_list_index-4:len(selected_list)]
selected_list.append('<{}>'.format(v[:2].lower()))
# 18 char to PURPLE : 18 - 29 ,18 after
if selected_list_index > 4:
selected_list = selected_list[selected_list_index - 4:len(selected_list)]
selected_list = ['--'] + selected_list
selected_string = ''.join(selected_list)
#if len(selected_string)<19:
# if len(selected_string)<19:
# selected_string += '-'*(21-len(selected_string))
#selected_string = selected_string[:30]
#wid = 19 #int(2+((len(display_modes)/2)*4))
output = ('-' * ((19) - (selected_list_index * 4))) + \
selected_string + \
('-' * (18 - ((len(display_modes) - selected_list_index - 1) * 4)))
# selected_string = selected_string[:30]
# wid = 19 #int(2+((len(display_modes)/2)*4))
output = ('-' * ((19) - (selected_list_index * 4))) + \
selected_string + \
('-' * (18 - ((len(display_modes) - selected_list_index - 1) * 4)))
output = output[0:46]
return output
@@ -639,11 +637,12 @@ class Display(object):
last_refreshed = 0
REFRESH_LIMIT = 100
def refresh_display(self):
if self.data.update_screen:
if time.time()*1000 - self.last_refreshed < self.REFRESH_LIMIT:
if time.time() * 1000 - self.last_refreshed < self.REFRESH_LIMIT:
return
self.last_refreshed = time.time()*1000
self.last_refreshed = time.time() * 1000
self.display_text.configure(state='normal')
self.display_text.delete(1.0, END)
self._load_display()

View File

@@ -1,16 +1,17 @@
import os
class Menu(object):
def __init__(self, data, message_handler, menu_height):
self.data = data
self.message_handler = message_handler
self.message_handler = message_handler
self.open_folders = []
self.menu_list = []
self.menu_height = menu_height
self.top_menu_index = 0
self.selected_list_index = 0
def navigate_menu_up(self):
if self.selected_list_index != 0:
if self.selected_list_index == self.top_menu_index:
@@ -27,7 +28,7 @@ class Menu(object):
if self.top_menu_index < self.menu_height:
self.top_menu_index = 0
self.selected_list_index = 0
else:
else:
self.top_menu_index -= self.menu_height
self.selected_list_index -= self.menu_height
print('self.top_menu_index ', self.top_menu_index)
@@ -50,20 +51,19 @@ class Menu(object):
self.top_menu_index += self.menu_height
self.selected_list_index = min(self.menu_height + self.selected_list_index, len(self.menu_list) - 1)
def update_open_folders(self, folder_name):
if folder_name not in self.open_folders:
self.open_folders.append(folder_name)
else:
self.open_folders.remove(folder_name)
def _check_folder_state(self, folder_name):
######## used for displaying folders as open or closed ########
if folder_name in self.open_folders:
return True, '/'
else:
return False, '|'
@staticmethod
def extract_file_type_and_name_from_menu_format(dir_name):
# removes whitespace and folder state from display item ########
@@ -72,7 +72,8 @@ class Menu(object):
else:
return True, dir_name.lstrip()
class BrowserMenu(Menu):
class BrowserMenu(Menu):
def __init__(self, data, message_handler, menu_height):
Menu.__init__(self, data, message_handler, menu_height)
self.generate_browser_list()
@@ -82,7 +83,7 @@ class BrowserMenu(Menu):
self.menu_list = []
for path in self.data.PATHS_TO_BROWSER:
self._add_folder_to_browser_list(path, 0)
for browser_line in self.menu_list:
is_file, name = self.extract_file_type_and_name_from_menu_format(browser_line['name'])
if is_file:
@@ -116,13 +117,12 @@ class BrowserMenu(Menu):
for slot_index, slot in enumerate(bank):
print('&&&&&&&&&&&&', slot)
if file_name == slot['name']:
return True, '{}-{}'.format(bank_index,slot_index)
return True, '{}-{}'.format(bank_index, slot_index)
return False, ''
def enter_on_browser_selection(self):
is_file, name = self.extract_file_type_and_name_from_menu_format(
self.menu_list[self.selected_list_index]['name'])
self.menu_list[self.selected_list_index]['name'])
if is_file:
is_successful = self.data.create_new_slot_mapping_in_first_open(name)
if not is_successful:
@@ -133,17 +133,16 @@ class BrowserMenu(Menu):
class SettingsMenu(Menu):
FOLDER_ORDER = ['video', 'sampler', 'user_input', 'capture', 'shader', 'detour', 'system' ]
SAMPLER_ORDER = ['LOOP_TYPE', 'LOAD_NEXT', 'RAND_START_MODE', 'RESET_PLAYERS', 'FIXED_LENGTH_MODE', 'FIXED_LENGTH', 'FIXED_LENGTH_MULTIPLY' ]
FOLDER_ORDER = ['video', 'sampler', 'user_input', 'capture', 'shader', 'detour', 'system']
SAMPLER_ORDER = ['LOOP_TYPE', 'LOAD_NEXT', 'RAND_START_MODE', 'RESET_PLAYERS', 'FIXED_LENGTH_MODE', 'FIXED_LENGTH', 'FIXED_LENGTH_MULTIPLY']
VIDEO_ORDER = ['VIDEOPLAYER_BACKEND']
USER_INPUT_ORDER = ['MIDI_INPUT', 'MIDI_STATUS', 'CYCLE_MIDI_PORT', 'OSC_INPUT', 'ACCESS_POINT', 'REMOTE_SERVER', 'SHOW_IP' ]
USER_INPUT_ORDER = ['MIDI_INPUT', 'MIDI_STATUS', 'CYCLE_MIDI_PORT', 'OSC_INPUT', 'ACCESS_POINT', 'REMOTE_SERVER', 'SHOW_IP']
CAPTURE_ORDER = ['DEVICE', 'TYPE']
SHADER_ORDER = ['USER_SHADER']
DETOUR_ORDER = ['TRY_DEMO']
SYSTEM_ORDER = []
SETTINGS_TO_HIDE = ['OUTPUT' ]
SETTINGS_TO_HIDE = ['OUTPUT']
def __init__(self, data, message_handler, menu_height):
@@ -159,16 +158,16 @@ class SettingsMenu(Menu):
if setting_folder_key in self.open_folders:
self.menu_list.append(dict(name='{}/'.format(setting_folder_key), value=''))
order_list_name = '{}_ORDER'.format(setting_folder_key.upper())
ordered_value = self.order_keys_from_list(setting_folder_item, getattr(self,order_list_name))
ordered_value = self.order_keys_from_list(setting_folder_item, getattr(self, order_list_name))
for (setting_details_key, setting_details_item) in ordered_value:
if not setting_details_key in self.SETTINGS_TO_HIDE:
self.menu_list.append(dict(name=' {}'.format(setting_details_key), value=self.data.make_empty_if_none(setting_details_item['value'])))
else:
else:
self.menu_list.append(dict(name='{}|'.format(setting_folder_key), value=''))
def enter_on_setting_selection(self):
is_file, name = self.extract_file_type_and_name_from_menu_format(
self.menu_list[self.selected_list_index]['name'])
self.menu_list[self.selected_list_index]['name'])
if is_file:
folder, setting_name, setting_details = self.data.get_setting_and_folder_from_name(name)
if setting_details['value'] in setting_details['options']:
@@ -195,7 +194,7 @@ class SettingsMenu(Menu):
for order_key in order_list:
if order_key in dictionary:
ordered_tuple_list.append((order_key, dictionary[order_key]))
for other_key in sorted(dictionary):
for other_key in sorted(dictionary):
if other_key not in [i[0] for i in ordered_tuple_list]:
ordered_tuple_list.append((other_key, dictionary[other_key]))
return ordered_tuple_list
@@ -219,8 +218,8 @@ class ShadersMenu(Menu):
def __init__(self, data, message_handler, menu_height):
Menu.__init__(self, data, message_handler, menu_height)
#self.top_menu_index = 1
#self.selected_list_index = 1
# self.top_menu_index = 1
# self.selected_list_index = 1
def generate_raw_shaders_list(self):
######## starts the recursive process of listing all folders and shader files to display ########
@@ -228,11 +227,10 @@ class ShadersMenu(Menu):
for path in self.data.PATHS_TO_SHADERS:
self._add_folder_to_shaders_list(path, 0)
return self.menu_list
def _add_folder_to_shaders_list(self, current_path, current_level):
######## adds the folders and shader files at the current level to the results list. recursively recalls at deeper level if folder is open ########
root, dirs, files = next(os.walk(current_path))
indent = ' ' * 4 * (current_level)
@@ -249,8 +247,3 @@ class ShadersMenu(Menu):
split_name = os.path.splitext(f)
if (split_name[1].lower() in ['.frag', '.shader', '.glsl', '.glslf', '.fsh']):
self.menu_list.append(dict(name='{}{}'.format(indent, f), is_shader=True))

View File

@@ -1,5 +1,6 @@
import logging
class MessageHandler(object):
def __init__(self):
self.current_message = [None, None, None]

View File

@@ -1,8 +1,8 @@
from pythonosc import osc_message_builder
from pythonosc import udp_client
from pythonosc import dispatcher
import argparse
from pythonosc import udp_client
def setup_osc_client(ip, port):
client_parser = argparse.ArgumentParser()
client_parser.add_argument("--ip", default=ip, help="the ip")
@@ -12,8 +12,9 @@ def setup_osc_client(ip, port):
return udp_client.SimpleUDPClient(client_args.ip, client_args.port)
client = setup_osc_client('127.0.0.1',5433)
client = setup_osc_client('127.0.0.1', 5433)
client.send_message("/shutdown", True)
client = setup_osc_client('127.0.0.1',9000)
client = setup_osc_client('127.0.0.1', 9000)
client.send_message("/shutdown", True)

View File

@@ -1,9 +1,9 @@
import math
import data_centre.plugin_collection
from data_centre.plugin_collection import ActionsPlugin, SequencePlugin, DisplayPlugin, AutomationSourcePlugin
class LFOModulationPlugin(ActionsPlugin,SequencePlugin,DisplayPlugin, AutomationSourcePlugin):
from data_centre.plugin_collection import ActionsPlugin, AutomationSourcePlugin, DisplayPlugin, SequencePlugin
class LFOModulationPlugin(ActionsPlugin, SequencePlugin, DisplayPlugin, AutomationSourcePlugin):
MAX_LFOS = 4
PRESET_FILE_NAME = "LFOModulationPlugin/config.json"
@@ -15,7 +15,7 @@ class LFOModulationPlugin(ActionsPlugin,SequencePlugin,DisplayPlugin, Automation
selected_lfo = 0
# for keeping track of LFO levels
level = [0.0]*MAX_LFOS
level = [0.0] * MAX_LFOS
speed = 0.5
# TODO: enable assigning of LFOs to mod slots
@@ -25,17 +25,18 @@ class LFOModulationPlugin(ActionsPlugin,SequencePlugin,DisplayPlugin, Automation
stop_flag = False
pause_flag = False
def __init__(self, plugin_collection):
super().__init__(plugin_collection)
#self.PRESET_FILE_NAME = "ShaderLoopRecordPlugin/frames.json"
# self.PRESET_FILE_NAME = "ShaderLoopRecordPlugin/frames.json"
self.presets = self.load_presets()
self.level = self.presets.get('levels', [0.0]*self.MAX_LFOS).copy()
self.level = self.presets.get('levels', [0.0] * self.MAX_LFOS).copy()
self.active = self.presets.get('active', False)
self.set_lfo_speed_direct(self.presets.get('speed', self.speed))
self.pc.shaders.root.after(1000, self.start_plugin)
def start_plugin(self):
super().start_plugin()
self.pc.shaders.root.after(0, self.run_automation)
@@ -46,30 +47,30 @@ class LFOModulationPlugin(ActionsPlugin,SequencePlugin,DisplayPlugin, Automation
def load_presets(self):
print("trying load presets? %s " % self.PRESET_FILE_NAME)
return self.pc.read_json(self.PRESET_FILE_NAME) or { 'levels': [0.0]*self.MAX_LFOS, 'active': self.active }
return self.pc.read_json(self.PRESET_FILE_NAME) or {'levels': [0.0] * self.MAX_LFOS, 'active': self.active}
def save_presets(self):
#for cmd,struct in self.commands.items():
# for cmd,struct in self.commands.items():
# self.presets.setdefault('modulation_levels',{})[cmd] = struct.get('modulation',[{},{},{},{}])
self.pc.update_json(self.PRESET_FILE_NAME, { 'levels': self.level.copy(), 'active': self.active, 'speed': self.speed } )
self.pc.update_json(self.PRESET_FILE_NAME, {'levels': self.level.copy(), 'active': self.active, 'speed': self.speed})
# DisplayPlugin methods
def get_display_modes(self):
return ['LFOMODU','NAV_LFO']
return ['LFOMODU', 'NAV_LFO']
def show_plugin(self, display, display_mode):
from tkinter import Text, END
#super(DisplayPlugin).show_plugin(display, display_mode)
from tkinter import END
# super(DisplayPlugin).show_plugin(display, display_mode)
display.display_text.insert(END, '{} \n'.format(display.body_title))
display.display_text.insert(END, "LFOModulation is ")
display.display_text.insert(END, "ACTIVE" if self.active else "not active")
display.display_text.insert(END, "\tSpeed: {:4.1f}% {}\n\n".format(self.speed*100, display.get_speed_indicator(self.speed/2.0, convert=False)))
display.display_text.insert(END, "\tSpeed: {:4.1f}% {}\n\n".format(self.speed * 100, display.get_speed_indicator(self.speed / 2.0, convert=False)))
for lfo,value in enumerate(self.level):
display.display_text.insert(END, "*" if self.selected_lfo==lfo else " ")
display.display_text.insert(END, "lfo {} level: {:4.2f}% {}\t".format(lfo,value*100,display.get_bar(value)))
for lfo, value in enumerate(self.level):
display.display_text.insert(END, "*" if self.selected_lfo == lfo else " ")
display.display_text.insert(END, "lfo {} level: {:4.2f}% {}\t".format(lfo, value * 100, display.get_bar(value)))
display.display_text.insert(END, "{}\t{}\n".format(self.last_lfo_status[lfo], display.get_bar(self.last_lfo_value[lfo])))
display.display_text.insert(END, "\tslot %s\t%s\n" % (display.get_mod_slot_label(lfo), self.formula[lfo]))
@@ -79,9 +80,9 @@ class LFOModulationPlugin(ActionsPlugin,SequencePlugin,DisplayPlugin, Automation
# methods/vars for AutomationSourcePlugin
# a lot of the nitty-gritty handled in parent class, these are for interfacing to the plugin
def get_frame_data(self):
diff = { 'levels': self.level.copy(), 'speed': self.speed, 'active': self.active }
#self.last_record = {}
#print(">>> reporting frame data for rec\n\t%s" % diff)
diff = {'levels': self.level.copy(), 'speed': self.speed, 'active': self.active}
# self.last_record = {}
# print(">>> reporting frame data for rec\n\t%s" % diff)
return diff
def recall_frame_data(self, data):
@@ -89,7 +90,7 @@ class LFOModulationPlugin(ActionsPlugin,SequencePlugin,DisplayPlugin, Automation
return
# print(">>>>recall from data:\n\t%s\n" %data)
if data.get('levels') is not None:
for slot,level in enumerate(data.get('levels')):
for slot, level in enumerate(data.get('levels')):
self.set_lfo_modulation_level(slot, level)
if data.get('active') is not None:
self.active = data.get('active')
@@ -107,41 +108,43 @@ class LFOModulationPlugin(ActionsPlugin,SequencePlugin,DisplayPlugin, Automation
line += "active " if data.get('active') else 'inactive '
if data.get('speed') is not None:
line += self.pc.display.get_speed_indicator(data.get('speed'))
#print ("returning %s from %s" %(line, data))
# print ("returning %s from %s" %(line, data))
return line
# ActionsPlugin methods
@property
def parserlist(self):
return [
( r"^set_lfo_modulation_([0-3])_level$", self.set_lfo_modulation_level ),
( r"^toggle_lfo_active$", self.toggle_lfo_active ),
( r"^set_lfo_speed", self.set_lfo_speed ),
( r"^select_lfo$", self.select_lfo ),
( r"^select_previous_lfo$", self.select_previous_lfo ),
( r"^select_next_lfo$", self.select_next_lfo ),
( r"^increase_lfo_([0-3])_level$", self.increase_lfo_level ),
( r"^decrease_lfo_([0-3])_level$", self.decrease_lfo_level ),
( r"^increase_selected_lfo_level$", self.increase_selected_lfo_level ),
( r"^decrease_selected_lfo_level$", self.decrease_selected_lfo_level ),
( r"^increase_lfo_speed", self.increase_lfo_speed ),
( r"^decrease_lfo_speed", self.decrease_lfo_speed ),
# TODO: changing formulas and LFO modes
return [
(r"^set_lfo_modulation_([0-3])_level$", self.set_lfo_modulation_level),
(r"^toggle_lfo_active$", self.toggle_lfo_active),
(r"^set_lfo_speed", self.set_lfo_speed),
(r"^select_lfo$", self.select_lfo),
(r"^select_previous_lfo$", self.select_previous_lfo),
(r"^select_next_lfo$", self.select_next_lfo),
(r"^increase_lfo_([0-3])_level$", self.increase_lfo_level),
(r"^decrease_lfo_([0-3])_level$", self.decrease_lfo_level),
(r"^increase_selected_lfo_level$", self.increase_selected_lfo_level),
(r"^decrease_selected_lfo_level$", self.decrease_selected_lfo_level),
(r"^increase_lfo_speed", self.increase_lfo_speed),
(r"^decrease_lfo_speed", self.decrease_lfo_speed),
# TODO: changing formulas and LFO modes
]
def set_lfo_modulation_level(self, slot, value):
if (value<0.0): value = 0.0
if (value>1.0): value = 1.0
if (value < 0.0):
value = 0.0
if (value > 1.0):
value = 1.0
self.level[slot] = value
def set_lfo_speed(self, speed):
self.set_lfo_speed_direct(-4*(0.5-(speed)))
self.set_lfo_speed_direct(-4 * (0.5 - (speed)))
def set_lfo_speed_direct(self, speed):
self.speed = speed
if self.speed<-2.0:
if self.speed < -2.0:
self.speed = -2.0
if self.speed>2.0:
if self.speed > 2.0:
self.speed = 2.0
def toggle_lfo_active(self):
@@ -150,66 +153,73 @@ class LFOModulationPlugin(ActionsPlugin,SequencePlugin,DisplayPlugin, Automation
def select_lfo(self, lfo):
self.selected_lfo = lfo
if self.selected_lfo>=self.MAX_LFOS:
if self.selected_lfo >= self.MAX_LFOS:
self.selected_lfo = 0
if self.selected_lfo<0:
self.selected_lfo = self.MAX_LFOS-1
if self.selected_lfo < 0:
self.selected_lfo = self.MAX_LFOS - 1
def select_next_lfo(self):
self.select_lfo(self.selected_lfo+1)
self.select_lfo(self.selected_lfo + 1)
def select_previous_lfo(self):
self.select_lfo(self.selected_lfo-1)
self.select_lfo(self.selected_lfo - 1)
level_step = 0.125
def increase_lfo_level(self, slot):
self.set_lfo_modulation_level(slot, self.level[slot] + self.level_step)
def decrease_lfo_level(self, slot):
self.set_lfo_modulation_level(slot, self.level[slot] - self.level_step)
def increase_selected_lfo_level(self):
self.increase_lfo_level(self.selected_lfo)
def decrease_selected_lfo_level(self):
self.decrease_lfo_level(self.selected_lfo)
lfo_step = 0.25
def increase_lfo_speed(self):
self.set_lfo_speed_direct ( self.speed + self.lfo_step )
if self.speed==0.0: # dont rest on 0 - set to a small amount forward
self.set_lfo_speed_direct(self.speed + self.lfo_step)
if self.speed == 0.0: # dont rest on 0 - set to a small amount forward
self.speed = 0.05
def decrease_lfo_speed(self):
self.set_lfo_speed_direct ( self.speed - self.lfo_step )
if self.speed==0.0: # dont rest on 0 - set to a small amount forward
self.set_lfo_speed_direct(self.speed - self.lfo_step)
if self.speed == 0.0: # dont rest on 0 - set to a small amount forward
self.speed = -0.05
# Formula handling for generating automation
# mapping 0-3 to match the LFO
# TODO: save & load this to config file, make editable
formula = [
"f_sin",
"f_double_cos",
"f_invert_sin",
#"f_invert_double_cos",
"f_linear"
"f_sin",
"f_double_cos",
"f_invert_sin",
# "f_invert_double_cos",
"f_linear"
]
# run the formula for the stored lfo configuration
last_lfo_status = [None]*MAX_LFOS # for displaying status
last_lfo_value = [None]*MAX_LFOS
#lfo_speed = [1.0]*MAX_LFOS
last_lfo_status = [None] * MAX_LFOS # for displaying status
last_lfo_value = [None] * MAX_LFOS
# lfo_speed = [1.0]*MAX_LFOS
def getLFO(self, position, lfo):
lfo_value = getattr(self,self.formula[lfo])(position, self.level[lfo])
lfo_value = getattr(self, self.formula[lfo])(position, self.level[lfo])
self.last_lfo_value[lfo] = lfo_value
self.last_lfo_status[lfo] = "sent {:03.1f}%".format(lfo_value*100.0)
self.last_lfo_status[lfo] = "sent {:03.1f}%".format(lfo_value * 100.0)
return lfo_value
# built-in waveshapes
# outgoing values should be between 0 and 1!!
# todo: more of the these, and better!
def f_sin(self, position, level):
#return level * (( math.sin(position*math.pi)))
# return level * (( math.sin(position*math.pi)))
value = math.sin(position * math.pi * 2) / 2
value *= level
value += 0.5 # normalise to range 0 - 1
value += 0.5 # normalise to range 0 - 1
return value
@@ -217,7 +227,7 @@ class LFOModulationPlugin(ActionsPlugin,SequencePlugin,DisplayPlugin, Automation
return 1.0 - self.f_sin(position, level)
def f_double_cos(self, position, level):
return self.f_sin(math.cos(position*math.pi), level)
return self.f_sin(math.cos(position * math.pi), level)
def f_invert_double_cos(self, position, level):
return 1.0 - self.f_double_cos(position, level)
@@ -230,16 +240,16 @@ class LFOModulationPlugin(ActionsPlugin,SequencePlugin,DisplayPlugin, Automation
import time
now = time.time()
if self.pc.data.plugins is None: # not initialised yet
if self.pc.data.plugins is None: # not initialised yet
return
if not self.active: # output is disabled
if not self.active: # output is disabled
return
for lfo in range(0,self.MAX_LFOS):
for lfo in range(0, self.MAX_LFOS):
# TODO: this is where would use assignable amounts and average across multiple inputs
if self.level[lfo]>0.0:
if self.level[lfo] > 0.0:
self.pc.actions.call_method_name(
"modulate_param_%s_to_amount_continuous"%lfo,
"modulate_param_%s_to_amount_continuous" % lfo,
self.getLFO(position, lfo)
)

View File

@@ -1,7 +1,6 @@
import data_centre.plugin_collection
from data_centre.plugin_collection import ActionsPlugin, DisplayPlugin, ModulationReceiverPlugin#, SequencePlugin
#import math
from math import sin, cos, tan, log, exp, pi
from data_centre.plugin_collection import ActionsPlugin, DisplayPlugin, ModulationReceiverPlugin # , SequencePlugin
# import math
"""
add to midi or osc mapping
@@ -41,8 +40,8 @@ TODO: >> ?? invert|set_the_shader_param_0_layer_>>print_arguments>>set_variab
"""
class ManipulatePlugin(ActionsPlugin,DisplayPlugin,ModulationReceiverPlugin):
class ManipulatePlugin(ActionsPlugin, DisplayPlugin, ModulationReceiverPlugin):
DEBUG = False
def __init__(self, plugin_collection):
@@ -51,31 +50,32 @@ class ManipulatePlugin(ActionsPlugin,DisplayPlugin,ModulationReceiverPlugin):
# ActionsPlugin methods
@property
def parserlist(self):
return [
( r"(.*)&&(.*)", self.run_multi ), # split && first since they need to be processed separately
( r"^invert\|(.*)$", self.invert ),
( r"^f:(.*):\|(.*)$", self.formula ), # formula eg ```f:sin(x):|```
( r"^set_variable_([a-zA-Z0-9]+)$", self.set_variable ),
( r"^([A-Z0-9]+)>(.*)$", self.recall_variable ), # recall variable and pipe into righthand side eg ```VAR1>invert|set_the_shader_.....```
( r"^(.*)>\&(.*)$", self.run_multi ), # pick up piped commands that duplicate a chain of values last
return [
(r"(.*)&&(.*)", self.run_multi), # split && first since they need to be processed separately
(r"^invert\|(.*)$", self.invert),
(r"^f:(.*):\|(.*)$", self.formula), # formula eg ```f:sin(x):|```
(r"^set_variable_([a-zA-Z0-9]+)$", self.set_variable),
(r"^([A-Z0-9]+)>(.*)$", self.recall_variable), # recall variable and pipe into righthand side eg ```VAR1>invert|set_the_shader_.....```
(r"^(.*)>\&(.*)$", self.run_multi), # pick up piped commands that duplicate a chain of values last
]
# DisplayPlugin methods
def show_plugin(self, display, display_mode):
from tkinter import Text, END
#super(DisplayPlugin).show_plugin(display, display_mode)
from tkinter import END
# super(DisplayPlugin).show_plugin(display, display_mode)
display.display_text.insert(END, '{} \n'.format(display.body_title))
display.display_text.insert(END, "test from ManipulatePlugin!\n")
for key,value in self.variables.items():
for key, value in self.variables.items():
display.display_text.insert(END, "\t" + key + "\t{:03.2f}\n".format(value))
def get_display_modes(self):
return ["MANIPULA","NAV_MANI"] #"NAV_MANIPULATE"]
return ["MANIPULA", "NAV_MANI"] # "NAV_MANIPULATE"]
# Actions
def run_multi(self, action1, action2, value):
if self.DEBUG: print("ManipulatePlugin>> multi-running '%s' and '%s' with value %s" % (action1, action2, value))
if self.DEBUG:
print("ManipulatePlugin>> multi-running '%s' and '%s' with value %s" % (action1, action2, value))
self.pc.actions.call_method_name(action1, value)
self.pc.actions.call_method_name(action2, value)
@@ -91,16 +91,19 @@ class ManipulatePlugin(ActionsPlugin,DisplayPlugin,ModulationReceiverPlugin):
def formula(self, formula, action, value):
self.variables['x'] = value
if self.DEBUG: print("ManipulatePlugin>> evaluating formula `%s` with value `%s`" % (formula, value))
if self.DEBUG:
print("ManipulatePlugin>> evaluating formula `%s` with value `%s`" % (formula, value))
value = eval(formula, globals(), self.variables)
if self.DEBUG: print("ManipulatePlugin>> got evaluated value `%s`" % value)
if self.DEBUG:
print("ManipulatePlugin>> got evaluated value `%s`" % value)
self.pc.actions.call_method_name(
action, value
)
def set_variable(self, var_name, value):
if self.DEBUG: print("ManipulatePlugin>> set_variable (%s) to %s" % (var_name, value))
if self.DEBUG:
print("ManipulatePlugin>> set_variable (%s) to %s" % (var_name, value))
self.variables[var_name] = value
def get_variable(self, var_name, default):
@@ -110,16 +113,16 @@ class ManipulatePlugin(ActionsPlugin,DisplayPlugin,ModulationReceiverPlugin):
return default
def recall_variable(self, var_name, action, *args):
if self.DEBUG: print ("ManipulatePlugin>> recall_variable (%s) as %s" % (var_name,args))
if self.DEBUG:
print("ManipulatePlugin>> recall_variable (%s) as %s" % (var_name, args))
self.pc.actions.call_method_name(
action, self.variables.get(var_name)# + list(args)
action, self.variables.get(var_name) # + list(args)
)
# ModulationReceiverPlugin methods
# methods for ModulationReceiverPlugin - receives changes to the in-built modulation levels (-1 to +1)
def set_modulation_value(self, param, value):
# take modulation value and throw it to local parameter
if self.DEBUG: print("||||| ManipulatePlugin received set_modulation_value for param %s with value %s!" % (param, value))
if self.DEBUG:
print("||||| ManipulatePlugin received set_modulation_value for param %s with value %s!" % (param, value))
self.set_variable("MODVALUE%s" % ('ABCD'[param]), value)

View File

@@ -1,9 +1,10 @@
from data_centre import plugin_collection
from data_centre.plugin_collection import MidiFeedbackPlugin
import mido
from data_centre.plugin_collection import MidiFeedbackPlugin
class MidiFeedbackAPCKey25Plugin(MidiFeedbackPlugin):
#disabled = False
# disabled = False
status = {}
@@ -11,12 +12,12 @@ class MidiFeedbackAPCKey25Plugin(MidiFeedbackPlugin):
super().__init__(plugin_collection)
self.description = 'Outputs feedback to APC Key 25'
def get_note(self,action,default):
def get_note(self, action, default):
bind = self.pc.midi_input.find_binding_for_action(action)
if bind is not None and 'note_on' in bind:
return int(bind.split(' ')[1])
else:
print ("bind is %s, returning default" % bind)
print("bind is %s, returning default" % bind)
return default
def stop_plugin(self):
@@ -32,16 +33,16 @@ class MidiFeedbackAPCKey25Plugin(MidiFeedbackPlugin):
self.init_notes()
def init_notes(self):
self.NOTE_PLAY_SHADER = self.get_note('play_shader_0_0',32)
self.NOTE_SHADER_FEEDBACK = self.get_note('toggle_feedback',85)
self.NOTE_SCENE_LAUNCH_COLUMN = self.get_note('toggle_shader_layer_0',82)
self.NOTE_PLAY_SHADER = self.get_note('play_shader_0_0', 32)
self.NOTE_SHADER_FEEDBACK = self.get_note('toggle_feedback', 85)
self.NOTE_SCENE_LAUNCH_COLUMN = self.get_note('toggle_shader_layer_0', 82)
self.NOTE_MODULATION_COLUMN = self.get_note('select_shader_modulation_slot_0', self.NOTE_SCENE_LAUNCH_COLUMN)
self.NOTE_CAPTURE_PREVIEW = self.get_note('toggle_capture_preview', 86)
self.NOTE_CLIP_STATUS_ROW = self.get_note('toggle_automation_clip_0', 8)
self.NOTE_SHADER_PRESET_ROW = self.get_note('select_preset_0', 0)
self.NOTE_SHADER_LAYER_ON = [ #82, 83, 84
self.get_note('toggle_shader_layer_%i'%i, 82+i) for i in range(0,3)
]
self.NOTE_SHADER_LAYER_ON = [ # 82, 83, 84
self.get_note('toggle_shader_layer_%i' % i, 82 + i) for i in range(0, 3)
]
def supports_midi_feedback(self, device_name):
supported_devices = ['APC Key 25']
@@ -51,16 +52,16 @@ class MidiFeedbackAPCKey25Plugin(MidiFeedbackPlugin):
def set_status(self, command='note_on', note=None, velocity=None):
self.status[note] = {
'command': command,
'note': note,
'velocity': velocity
'command': command,
'note': note,
'velocity': velocity
}
#print("set status to %s: %s" % (note, self.status[note]))
# 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))
# print("send_command(%s, %s)" % (note, velocity))
self.midi_feedback_device.send(
mido.Message(command, note=note, velocity=velocity)
mido.Message(command, note=note, velocity=velocity)
)
def feedback_shader_feedback(self, on):
@@ -70,15 +71,16 @@ class MidiFeedbackAPCKey25Plugin(MidiFeedbackPlugin):
self.set_status(note=self.NOTE_CAPTURE_PREVIEW, velocity=int(on))
def feedback_shader_on(self, layer, slot, colour=None):
if colour is None: colour = self.COLOUR_GREEN
self.set_status(note=(self.NOTE_PLAY_SHADER-(layer)*8)+slot, velocity=int(colour))
if colour is None:
colour = self.COLOUR_GREEN
self.set_status(note=(self.NOTE_PLAY_SHADER - (layer) * 8) + slot, velocity=int(colour))
def feedback_shader_off(self, layer, slot):
self.set_status(note=(self.NOTE_PLAY_SHADER-(layer)*8)+slot, velocity=self.COLOUR_OFF)
self.set_status(note=(self.NOTE_PLAY_SHADER - (layer) * 8) + slot, velocity=self.COLOUR_OFF)
def feedback_shader_layer_on(self, layer):
self.set_status(note=self.NOTE_SHADER_LAYER_ON[layer], velocity=self.COLOUR_GREEN)
def feedback_shader_layer_off(self, layer):
self.set_status(note=self.NOTE_SHADER_LAYER_ON[layer], velocity=self.COLOUR_OFF)
@@ -86,90 +88,90 @@ class MidiFeedbackAPCKey25Plugin(MidiFeedbackPlugin):
self.set_status(note=70, velocity=layer)
def feedback_show_modulation(self, slot):
for i in range(self.NOTE_MODULATION_COLUMN,self.NOTE_MODULATION_COLUMN+4):
if slot==i-self.NOTE_MODULATION_COLUMN:
for i in range(self.NOTE_MODULATION_COLUMN, self.NOTE_MODULATION_COLUMN + 4):
if slot == i - self.NOTE_MODULATION_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 data_centre.plugin_collection import SequencePlugin
try:
from plugins.ShaderLoopRecordPlugin import ShaderLoopRecordPlugin
for plugin in self.pc.get_plugins(SequencePlugin):
if isinstance(plugin, ShaderLoopRecordPlugin):
try:
from plugins.ShaderLoopRecordPlugin import ShaderLoopRecordPlugin
for plugin in self.pc.get_plugins(SequencePlugin):
if isinstance(plugin, ShaderLoopRecordPlugin):
NOTE_PLAY_STATUS = 65
NOTE_RECORD_STATUS = 66
NOTE_OVERDUB_STATUS = 67
#NOTE_CLIP_STATUS_ROW = 8
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=self.NOTE_CLIP_STATUS_ROW+i, velocity=colour)
""" # doesnt really work well, but shows progress of clip in leds
if plugin.is_playing() and not plugin.is_paused():
for i in range(0,plugin.MAX_CLIPS):
if i==int(plugin.position*plugin.MAX_CLIPS):
self.set_status(command='note_on', note=self.NOTE_CLIP_STATUS_ROW+i, velocity=self.COLOUR_GREEN)
"""
except Exception as e:
pass
#print ("Warning: Failed when running plugin feedback for ShaderLoopRecordPlugin:\t%s" % str(e))
try:
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_OFF
if plugin.is_playing():
colour = self.COLOUR_GREEN
if plugin.selected_preset==pad:
if plugin.presets[pad] is None:
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
colour += self.BLINK
self.set_status(command='note_on', note=self.NOTE_SHADER_PRESET_ROW+pad, velocity=colour)
except Exception as e:
pass
#print ("Warning: Failed when running plugin feedback for ShaderQuickPresetPlugin:\t%s" % str(e))
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=self.NOTE_CLIP_STATUS_ROW + i, velocity=colour)
""" # doesnt really work well, but shows progress of clip in leds
if plugin.is_playing() and not plugin.is_paused():
for i in range(0,plugin.MAX_CLIPS):
if i==int(plugin.position*plugin.MAX_CLIPS):
self.set_status(command='note_on', note=self.NOTE_CLIP_STATUS_ROW+i, velocity=self.COLOUR_GREEN)
"""
except Exception as e:
pass
# print ("Warning: Failed when running plugin feedback for ShaderLoopRecordPlugin:\t%s" % str(e))
try:
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=self.NOTE_SHADER_PRESET_ROW + pad, velocity=colour)
except Exception as e:
pass
# print ("Warning: Failed when running plugin feedback for ShaderQuickPresetPlugin:\t%s" % str(e))
BLINK = 1
COLOUR_OFF = 0
@@ -188,7 +190,7 @@ class MidiFeedbackAPCKey25Plugin(MidiFeedbackPlugin):
# 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:
# 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)
@@ -203,16 +205,16 @@ class MidiFeedbackAPCKey25Plugin(MidiFeedbackPlugin):
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))
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:
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)
@@ -228,15 +230,16 @@ class MidiFeedbackAPCKey25Plugin(MidiFeedbackPlugin):
self.update_device()
#print("refresh_midi_feedback")
# 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 (i not in self.last_state or self.last_state[i]!=c):
#print("got command: %s: %s" % (i,c))
# 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 (i not in self.last_state or self.last_state[i] != c):
# print("got command: %s: %s" % (i,c))
self.send_command(**c)
self.last_state = deepcopy(self.status)

View File

@@ -1,11 +1,9 @@
from data_centre import plugin_collection
from data_centre.plugin_collection import MidiFeedbackPlugin
import mido
import plugins
#from plugins.MidiFeedbackAPCKey25Plugin import MidiFeedbackAPCKey25Plugin
# from plugins.MidiFeedbackAPCKey25Plugin import MidiFeedbackAPCKey25Plugin
class MidiFeedbackLaunchpadPlugin(plugins.MidiFeedbackAPCKey25Plugin.MidiFeedbackAPCKey25Plugin):
status = {}
def __init__(self, plugin_collection):
@@ -13,15 +11,15 @@ class MidiFeedbackLaunchpadPlugin(plugins.MidiFeedbackAPCKey25Plugin.MidiFeedbac
self.description = 'Outputs feedback to Novation Launchpad'
def init_notes(self):
self.NOTE_PLAY_SHADER = self.get_note('play_shader_0_0',0)
self.NOTE_SHADER_FEEDBACK = self.get_note('toggle_feedback',85)
self.NOTE_SCENE_LAUNCH_COLUMN = self.get_note('toggle_shader_layer_0',82)
self.NOTE_PLAY_SHADER = self.get_note('play_shader_0_0', 0)
self.NOTE_SHADER_FEEDBACK = self.get_note('toggle_feedback', 85)
self.NOTE_SCENE_LAUNCH_COLUMN = self.get_note('toggle_shader_layer_0', 82)
self.NOTE_MODULATION_COLUMN = self.get_note('select_shader_modulation_slot_0', self.NOTE_SCENE_LAUNCH_COLUMN)
self.NOTE_CAPTURE_PREVIEW = self.get_note('toggle_capture_preview', 86)
self.NOTE_CLIP_STATUS_ROW = self.get_note('toggle_automation_clip_0', 8)
self.NOTE_SHADER_PRESET_ROW = self.get_note('select_preset_0', 112)
self.NOTE_SHADER_LAYER_ON = [
self.get_note('toggle_shader_layer_%i'%i,8+(i*16)) for i in range(0,3)
self.get_note('toggle_shader_layer_%i' % i, 8 + (i * 16)) for i in range(0, 3)
]
def supports_midi_feedback(self, device_name):
@@ -31,16 +29,17 @@ class MidiFeedbackLaunchpadPlugin(plugins.MidiFeedbackAPCKey25Plugin.MidiFeedbac
return True
def feedback_shader_on(self, layer, slot, colour=None):
if colour is None: colour = self.COLOUR_GREEN
self.set_status(note=(self.NOTE_PLAY_SHADER+(layer)*16)+slot, velocity=int(colour))
if colour is None:
colour = self.COLOUR_GREEN
self.set_status(note=(self.NOTE_PLAY_SHADER + (layer) * 16) + slot, velocity=int(colour))
def feedback_shader_off(self, layer, slot):
self.set_status(note=(self.NOTE_PLAY_SHADER+(layer)*16)+slot, velocity=self.COLOUR_OFF)
self.set_status(note=(self.NOTE_PLAY_SHADER + (layer) * 16) + slot, velocity=self.COLOUR_OFF)
# TODO: make these colours correct+sensible
BLINK = 1
COLOUR_OFF = 0
COLOUR_GREEN = 8 #1
COLOUR_GREEN = 8 # 1
COLOUR_GREEN_BLINK = 15
COLOUR_RED = 32
COLOUR_RED_BLINK = 47

View File

@@ -1,14 +1,14 @@
import data_centre.plugin_collection
from data_centre.plugin_collection import ActionsPlugin
class MultiActionsPlugin(ActionsPlugin):
disabled = False # this is only a demo of very basic multi-actions plugin -- superceded by ManipulatePlugin
disabled = False # this is only a demo of very basic multi-actions plugin -- superceded by ManipulatePlugin
def __init__(self, plugin_collection):
super().__init__(plugin_collection)
try:
from plugins import ManipulatePlugin
self.disabled = True # if we've found ManipulatePlugin then disable this one
self.disabled = True # if we've found ManipulatePlugin then disable this one
except:
# if it fails, we're good to go (so long as not disabled explictly above)
pass
@@ -16,7 +16,7 @@ class MultiActionsPlugin(ActionsPlugin):
@property
def parserlist(self):
return [
( r"(.*)&&(.*)", self.run_multi ),
(r"(.*)&&(.*)", self.run_multi),
]
def run_multi(self, action1, action2, value):

View File

@@ -1,11 +1,10 @@
import data_centre.plugin_collection
from data_centre.plugin_collection import ActionsPlugin, SequencePlugin, DisplayPlugin
from data_centre.plugin_collection import ActionsPlugin, DisplayPlugin, SequencePlugin
from plugins.frame_manager import Frame
class ShaderLoopRecordPlugin(ActionsPlugin,SequencePlugin,DisplayPlugin):
class ShaderLoopRecordPlugin(ActionsPlugin, SequencePlugin, DisplayPlugin):
MAX_CLIPS = 8
frames = []
frames = []
def __init__(self, plugin_collection):
super().__init__(plugin_collection)
@@ -21,25 +20,25 @@ class ShaderLoopRecordPlugin(ActionsPlugin,SequencePlugin,DisplayPlugin):
self.reset_ignored()
def load_presets(self):
#try:
# 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.MAX_CLIPS:
print ("adding clip ")
p += self.get_empty_clip(self.duration) #[ [None] ] #*((int(self.duration / self.frequency))-len(p)) ]
while len(p) < self.MAX_CLIPS:
print("adding clip ")
p += self.get_empty_clip(self.duration) # [ [None] ] #*((int(self.duration / self.frequency))-len(p)) ]
for i in p:
print("got automation clip of duration %s" % len(i))
if i and len(i)<(int(self.duration / self.frequency)):
if i and len(i) < (int(self.duration / self.frequency)):
print("adding more slots due to size change")
i += [None]*((int(self.duration / self.frequency))-len(i))
i += [None] * ((int(self.duration / self.frequency)) - len(i))
print("len is now %s" % len(i))
return p
elif p:
return p
else:
return self.get_factory_reset()
#except:
# except:
# return self.clear_frames()
def save_presets(self):
@@ -51,72 +50,69 @@ class ShaderLoopRecordPlugin(ActionsPlugin,SequencePlugin,DisplayPlugin):
# DisplayPlugin methods
def get_display_modes(self):
return ['LOOPREC','NAV_LPRC']
return ['LOOPREC', 'NAV_LPRC']
def show_plugin(self, display, display_mode):
from tkinter import Text, END
#super(DisplayPlugin).show_plugin(display, display_mode)
from tkinter import END
# super(DisplayPlugin).show_plugin(display, display_mode)
display.display_text.insert(END, '{} \n'.format(display.body_title))
display.display_text.insert(END, "test from ShaderLoopRecordPlugin!\n")
status = ""
status+="playing" if self.is_playing() else "stopped"
status+="\t"
status+="paused" if self.is_paused() else "unpaused"
status+="\t"
status+="REC" if self.recording else "---"
status+="*" if self.is_ignoring() else " "
status+="\t"
status+="Overdub" if self.overdub else "-------"
status+="\t"
status+="Looping" if self.looping else "Once"
status+="\n"
status += "playing" if self.is_playing() else "stopped"
status += "\t"
status += "paused" if self.is_paused() else "unpaused"
status += "\t"
status += "REC" if self.recording else "---"
status += "*" if self.is_ignoring() else " "
status += "\t"
status += "Overdub" if self.overdub else "-------"
status += "\t"
status += "Looping" if self.looping else "Once"
status += "\n"
display.display_text.insert(END, status)
display.display_text.insert(END, ("Position:\t{:3.0f}%\t[{:12s}]".format(self.position*100.0,("#"*int(self.position*12)))))
#display.display_text.insert(END, (" Speed: {:03.2f}%\n".format(self.speed*100)))
display.display_text.insert(END, (" Speed:{} {:3.0f}%\n".format(display.get_speed_indicator(self.speed/2,convert=False),self.speed*100)))
if self.speed==0.0:
display.display_text.insert(END, ("Position:\t{:3.0f}%\t[{:12s}]".format(self.position * 100.0, ("#" * int(self.position * 12)))))
# display.display_text.insert(END, (" Speed: {:03.2f}%\n".format(self.speed*100)))
display.display_text.insert(END, (" Speed:{} {:3.0f}%\n".format(display.get_speed_indicator(self.speed / 2, convert=False), self.speed * 100)))
if self.speed == 0.0:
display.display_text.insert(END, ("Duration:\tinfinity!\n"))
else:
display.display_text.insert(END, ("Duration:\t{:03.2f}s\n".format(((self.duration/1000)/self.speed)/4)))
#distance s = d/t d = s*t t = d/s
display.display_text.insert(END, ("Duration:\t{:03.2f}s\n".format(((self.duration / 1000) / self.speed) / 4)))
# distance s = d/t d = s*t t = d/s
status = "\nEnabled clips:\t"
for i in range(0,self.MAX_CLIPS):
status+="#" if i==self.selected_clip and i in self.running_clips else\
"/" if i==self.selected_clip and i not in self.running_clips else\
"=" if i in self.running_clips else\
'_'
for i in range(0, self.MAX_CLIPS):
status += "#" if i == self.selected_clip and i in self.running_clips else \
"/" if i == self.selected_clip and i not in self.running_clips else \
"=" if i in self.running_clips else \
'_'
status += "\n"
display.display_text.insert(END, status)
"""for key,value in self.variables.items():
display.display_text.insert(END, "\t" + key + "\t{:03.2f}\n".format(value))"""
@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 ),
( r"set_automation_speed", self.set_speed ),
( r"toggle_record_automation", self.toggle_record_automation ),
( r"toggle_overdub_automation", self.toggle_overdub_automation ),
( r"clear_automation", self.clear_clip ),
( r"select_automation_clip_([0-7])", self.select_clip ),
( r"toggle_automation_clip_([0-7])", self.toggle_clip ),
#( r"smooth_clip_(0-7])", self.smooth_clip ),
( r"smooth_selected_clip", self.smooth_selected_clip )
(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),
(r"set_automation_speed", self.set_speed),
(r"toggle_record_automation", self.toggle_record_automation),
(r"toggle_overdub_automation", self.toggle_overdub_automation),
(r"clear_automation", self.clear_clip),
(r"select_automation_clip_([0-7])", self.select_clip),
(r"toggle_automation_clip_([0-7])", self.toggle_clip),
# ( r"smooth_clip_(0-7])", self.smooth_clip ),
(r"smooth_selected_clip", self.smooth_selected_clip)
]
def smooth_selected_clip(self):
self.pc.fm.interpolate_clip(self.frames[self.selected_clip])
def toggle_overdub_automation(self):
self.overdub = not self.overdub
if not self.overdub:
@@ -132,36 +128,37 @@ class ShaderLoopRecordPlugin(ActionsPlugin,SequencePlugin,DisplayPlugin):
self.last_saved_index = None
self.save_presets()
def get_empty_clip(self, duration = 4000):
def get_empty_clip(self, duration=4000):
return [None] * (int(duration / self.frequency))
def get_factory_reset(self):
return [ self.get_empty_clip(self.duration) for i in range(self.MAX_CLIPS) ]
return [self.get_empty_clip(self.duration) for i in range(self.MAX_CLIPS)]
def clear_clip(self,clip = None):
def clear_clip(self, clip=None):
if clip is None:
clip = self.selected_clip
self.frames[clip] = self.get_empty_clip(self.duration) * self.MAX_CLIPS
self.reset_ignored()
if self.DEBUG_FRAMES: print ("clear_frames set to %s" % (int(self.duration / self.frequency)))
if self.DEBUG_FRAMES:
print("clear_frames set to %s" % (int(self.duration / self.frequency)))
return self.frames
def toggle_clip(self,clip = None):
def toggle_clip(self, clip=None):
if clip is None:
clip = self.selected_clip
else:
self.selected_clip = clip
#self.running_clips[clip] = not self.running_clips[clip]
# self.running_clips[clip] = not self.running_clips[clip]
if clip in self.running_clips:
self.running_clips.remove(clip)
else:
self.running_clips.append(clip)
print("running clips looks like %s" %self.running_clips)
print("running clips looks like %s" % self.running_clips)
def reset_ignored(self):
# print("!!!!resetting ignored")
self.ignored = Frame(self.pc).store_copy({ 'shader_params': [[None]*4,[None]*4,[None]*4] })
self.ignored = Frame(self.pc).store_copy({'shader_params': [[None] * 4, [None] * 4, [None] * 4]})
def is_ignoring(self):
return not self.pc.fm.is_frame_empty(self.ignored)
@@ -170,81 +167,92 @@ class ShaderLoopRecordPlugin(ActionsPlugin,SequencePlugin,DisplayPlugin):
self.selected_clip = clip
selected_clip = 0
running_clips = [ ] #False ] * self.MAX_CLIPS
running_clips = [] # False ] * self.MAX_CLIPS
duration = 4000
frequency = 10 #25
frequency = 10 # 25
recording = False
overdub = True
#ignored = None # set in reset_ignored in init - used for tracking what parans have changed since overdub
last_frame = None # for tracking what's changed between frames when overdubbing
last_saved_index = None # for backfilling
DEBUG_FRAMES = False#True
overdub = True
# ignored = None # set in reset_ignored in init - used for tracking what parans have changed since overdub
last_frame = None # for tracking what's changed between frames when overdubbing
last_saved_index = None # for backfilling
DEBUG_FRAMES = False # True
def run_sequence(self, position):
current_frame_index = int(position * (int(self.duration / self.frequency)))
if current_frame_index<0:
current_frame_index = (self.duration/self.frequency) - current_frame_index
if current_frame_index >= self.duration: # self.duration/self.frequency:
current_frame_index = "(self.duration/self.frequency) +""" (current_frame_index%self.duration)
if current_frame_index < 0:
current_frame_index = (self.duration / self.frequency) - current_frame_index
if current_frame_index >= self.duration: # self.duration/self.frequency:
current_frame_index = "(self.duration/self.frequency) +"""(current_frame_index % self.duration)
if self.DEBUG_FRAMES: print (">>>>>>>>>>>>>>frame at %i%%: %i" % (position*100, current_frame_index))
#print("got frame index %s" % current_frame_index)
if self.DEBUG_FRAMES:
print(">>>>>>>>>>>>>>frame at %i%%: %i" % (position * 100, current_frame_index))
# print("got frame index %s" % current_frame_index)
if self.recording:
current_frame = self.pc.fm.get_live_frame() #.copy()
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))
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]
# 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 ]
self.running_clips += [self.selected_clip]
if self.recording:
current_frame = self.pc.fm.get_live_frame() #.copy()
if self.DEBUG_FRAMES: print("current_frame copy before recall is %s" % current_frame['shader_params'])
current_frame = self.pc.fm.get_live_frame() # .copy()
if self.DEBUG_FRAMES:
print("current_frame copy before recall is %s" % current_frame['shader_params'])
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'])
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)
if self.DEBUG_FRAMES: print("about to call get_ignored_frames with %s\n and\n %s" % (saved_frame.f, self.ignored.f))
"""print("got self.ignored:\n\t%s\n" % self.ignored.f)
print("diff is currently:\n\t%s\n" % diff.f)
print("saved_frame is currently:\n\t%s\n" % saved_frame.f)"""
diff = self.pc.fm.merge_frames(
self.pc.fm.get_frame_ignored(saved_frame, self.ignored),
diff
)
#print("got merged:\n\t%s\n" % diff.f)
#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)
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)
if self.DEBUG_FRAMES:
print("about to call get_ignored_frames with %s\n and\n %s" % (saved_frame.f, self.ignored.f))
"""print("got self.ignored:\n\t%s\n" % self.ignored.f)
print("diff is currently:\n\t%s\n" % diff.f)
print("saved_frame is currently:\n\t%s\n" % saved_frame.f)"""
diff = self.pc.fm.merge_frames(
self.pc.fm.get_frame_ignored(saved_frame, self.ignored),
diff
)
# print("got merged:\n\t%s\n" % diff.f)
# 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())"""

View File

@@ -1,9 +1,8 @@
import data_centre.plugin_collection
from data_centre.plugin_collection import ActionsPlugin, SequencePlugin, DisplayPlugin
import copy
from data_centre.plugin_collection import ActionsPlugin, DisplayPlugin
from plugins.frame_manager import Frame
class ShaderQuickPresetPlugin(ActionsPlugin,DisplayPlugin): #,SequencePlugin):
class ShaderQuickPresetPlugin(ActionsPlugin, DisplayPlugin): # ,SequencePlugin):
MAX_PRESETS = 8
display_live_on = False
@@ -20,7 +19,7 @@ class ShaderQuickPresetPlugin(ActionsPlugin,DisplayPlugin): #,SequencePlugin):
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)) ]
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)
@@ -31,20 +30,20 @@ class ShaderQuickPresetPlugin(ActionsPlugin,DisplayPlugin): #,SequencePlugin):
@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 ),
( r"qksh_toggle_display_live", self.toggle_display_live ),
( r"switch_to_next_preset", self.switch_to_next_preset ),
( r"switch_to_previous_preset", self.switch_to_previous_preset ),
( r"switch_to_current_preset", self.switch_to_current_preset ),
( r"select_previous_preset", self.select_previous_preset ),
( r"select_next_preset", self.select_next_preset ),
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),
(r"qksh_toggle_display_live", self.toggle_display_live),
(r"switch_to_next_preset", self.switch_to_next_preset),
(r"switch_to_previous_preset", self.switch_to_previous_preset),
(r"switch_to_current_preset", self.switch_to_current_preset),
(r"select_previous_preset", self.select_previous_preset),
(r"select_next_preset", self.select_next_preset),
]
def toggle_display_live(self):
@@ -52,16 +51,16 @@ class ShaderQuickPresetPlugin(ActionsPlugin,DisplayPlugin): #,SequencePlugin):
# DisplayPlugin methods
def get_display_modes(self):
return ['QUIKSHDR',['NAV_QKSH','PLAY_SHADER']]
return ['QUIKSHDR', ['NAV_QKSH', 'PLAY_SHADER']]
def show_plugin(self, display, display_mode):
from tkinter import Text, END
#super(DisplayPlugin).show_plugin(display, display_mode)
from tkinter import END
# super(DisplayPlugin).show_plugin(display, display_mode)
display.display_text.insert(END, '{} \n'.format(display.body_title))
display.display_text.insert(END, "ShaderQuickPresetPlugin")
status = "Selected: ["
#for i,preset in enumerate(self.presets):
# for i,preset in enumerate(self.presets):
for i in range(self.MAX_PRESETS):
preset = self.presets[i]
if i == self.selected_preset:
@@ -83,14 +82,14 @@ class ShaderQuickPresetPlugin(ActionsPlugin,DisplayPlugin): #,SequencePlugin):
display.display_text.insert(END, "Showing LIVE preview\n")
else:
display.display_text.insert(END, "Showing stored preset slot %s" % self.selected_preset)
if self.selected_preset==self.last_recalled:
if self.selected_preset == self.last_recalled:
display.display_text.insert(END, " [last switched]")
display.display_text.insert(END, "\n")
## show a summary of the selected preset
if self.selected_preset is not None:
# TODO: switch to display current settings
#for line in self.pc.fm.get_live_frame().get_frame_summary():
# for line in self.pc.fm.get_live_frame().get_frame_summary():
for line in (self.presets[self.selected_preset] if not self.display_live_on else self.pc.fm.get_live_frame()).get_frame_summary():
display.display_text.insert(END, "%s\n" % line)
@@ -101,7 +100,7 @@ class ShaderQuickPresetPlugin(ActionsPlugin,DisplayPlugin): #,SequencePlugin):
# didnt find an empty slot, save to current
self.store_current_preset()
self.selected_preset += 1
self.selected_preset %= self.MAX_PRESETS
self.selected_preset %= self.MAX_PRESETS
else:
# found an empty slot, save to it
self.selected_preset = res[0]
@@ -115,11 +114,12 @@ class ShaderQuickPresetPlugin(ActionsPlugin,DisplayPlugin): #,SequencePlugin):
self.save_presets()
def store_current_preset(self):
if self.selected_preset is None: self.selected_preset = 0
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))
# print ("stored %s at position %s" % (self.presets[insert_position], insert_position))
self.selected_preset = insert_position
self.last_recalled = insert_position
@@ -129,18 +129,18 @@ class ShaderQuickPresetPlugin(ActionsPlugin,DisplayPlugin): #,SequencePlugin):
self.selected_preset = preset
def switch_to_preset(self, preset):
#if preset>len(self.presets):
# if preset>len(self.presets):
if self.presets[preset] is None:
self.message_handler.set_message('ERROR',"No quick shader preset in slot %s!" % preset)
self.message_handler.set_message('ERROR', "No quick shader preset in slot %s!" % preset)
self.selected_preset = preset
return
print ("switching to preset %s" % preset)
print("switching to preset %s" % preset)
self.selected_preset = preset
self.last_recalled = preset
preset = self.presets[preset]
print ("recalled preset %s" % preset)
print("recalled preset %s" % preset)
self.pc.fm.recall_frame(preset)
def switch_to_current_preset(self):
@@ -160,7 +160,7 @@ class ShaderQuickPresetPlugin(ActionsPlugin,DisplayPlugin): #,SequencePlugin):
self.selected_preset = 0
return
self.selected_preset += 1
if self.selected_preset>=self.MAX_PRESETS:
if self.selected_preset >= self.MAX_PRESETS:
self.selected_preset = 0
def select_previous_preset(self):
@@ -168,8 +168,5 @@ class ShaderQuickPresetPlugin(ActionsPlugin,DisplayPlugin): #,SequencePlugin):
self.selected_preset = self.MAX_PRESETS
return
self.selected_preset -= 1
if self.selected_preset<0:
self.selected_preset = self.MAX_PRESETS-1
if self.selected_preset < 0:
self.selected_preset = self.MAX_PRESETS - 1

View File

@@ -1,18 +1,16 @@
import math
import data_centre.plugin_collection
from data_centre.plugin_collection import ActionsPlugin, SequencePlugin, DisplayPlugin, AutomationSourcePlugin
import pyaudio
import numpy as np
from random import randint
from statistics import mean
#import matplotlib.pyplot as plt
import numpy as np
import pyaudio
np.set_printoptions(suppress=True) # don't use scientific notationn
from data_centre.plugin_collection import ActionsPlugin, DisplayPlugin, SequencePlugin
class SoundReactPlugin(ActionsPlugin,SequencePlugin,DisplayPlugin):
# import matplotlib.pyplot as plt
np.set_printoptions(suppress=True) # don't use scientific notationn
class SoundReactPlugin(ActionsPlugin, SequencePlugin, DisplayPlugin):
DEBUG = False
active = True
@@ -21,15 +19,15 @@ class SoundReactPlugin(ActionsPlugin,SequencePlugin,DisplayPlugin):
stream = None
CHUNK = 4096 # number of data points to read at a time
RATE = 48000 #44100 # time resolution of the recording device (Hz)
frequency = 10 # how often messages are sampled+calculated+sent, not anything to do with audio frequency
CHUNK = 4096 # number of data points to read at a time
RATE = 48000 # 44100 # time resolution of the recording device (Hz)
frequency = 10 # how often messages are sampled+calculated+sent, not anything to do with audio frequency
values = {}
levels = {
"energy": [ 0.0, 0.0, 1.0, 0.0 ],
"peakfreq": [ 0.0, 0.0, 0.0, 0.0 ]
"energy": [0.0, 0.0, 1.0, 0.0],
"peakfreq": [0.0, 0.0, 0.0, 0.0]
}
last_values = {}
display_values = {}
@@ -46,9 +44,9 @@ class SoundReactPlugin(ActionsPlugin,SequencePlugin,DisplayPlugin):
self.PRESET_FILE_NAME = "SoundReactPlugin/config.json"
presets = self.load_presets()
self.config = presets.get('config',self.config)
self.levels = presets.get('levels',self.levels)
self.active = presets.get('active',self.active)
self.config = presets.get('config', self.config)
self.levels = presets.get('levels', self.levels)
self.active = presets.get('active', self.active)
if not self.disabled:
self.start_plugin()
@@ -58,7 +56,7 @@ class SoundReactPlugin(ActionsPlugin,SequencePlugin,DisplayPlugin):
return self.pc.read_json(self.PRESET_FILE_NAME) or {}
def save_presets(self):
self.pc.update_json(self.PRESET_FILE_NAME, { 'config': self.config, 'levels': self.levels, 'active': self.active } )
self.pc.update_json(self.PRESET_FILE_NAME, {'config': self.config, 'levels': self.levels, 'active': self.active})
def stop_plugin(self):
super().stop_plugin()
@@ -75,9 +73,9 @@ class SoundReactPlugin(ActionsPlugin,SequencePlugin,DisplayPlugin):
def open_sound_device(self):
try:
self.p=pyaudio.PyAudio()
self.stream=self.p.open(format=pyaudio.paInt16,channels=1,rate=self.RATE,input=True,
frames_per_buffer=self.CHUNK)
self.p = pyaudio.PyAudio()
self.stream = self.p.open(format=pyaudio.paInt16, channels=1, rate=self.RATE, input=True,
frames_per_buffer=self.CHUNK)
except:
print("Failed to open sound device - disabling SoundReactPlugin!")
self.active = False
@@ -98,10 +96,10 @@ class SoundReactPlugin(ActionsPlugin,SequencePlugin,DisplayPlugin):
# TODO: write more interpreters
return {
"energy": self.energy,
#"low": self.low,
#"mid": self.mid,
#"high": self.high,
#"peakfreq": self.peakfreq
# "low": self.low,
# "mid": self.mid,
# "high": self.high,
# "peakfreq": self.peakfreq
}
# triggers?
@@ -109,127 +107,128 @@ class SoundReactPlugin(ActionsPlugin,SequencePlugin,DisplayPlugin):
# DisplayPlugin methods
def get_display_modes(self):
return ['SOUNDMOD','NAV_SND']
return ['SOUNDMOD', 'NAV_SND']
def show_plugin(self, display, display_mode):
from tkinter import Text, END
#super(DisplayPlugin).show_plugin(display, display_mode)
from tkinter import END
# super(DisplayPlugin).show_plugin(display, display_mode)
display.display_text.insert(END, '{} \n'.format(display.body_title))
display.display_text.insert(END, "SoundReactPlugin - ")
display.display_text.insert(END, "ACTIVE\n" if self.active else "not active\n")
#display.display_text.insert(END, "\tSpeed: {:03.2f}\n\n".format(self.speed))
# display.display_text.insert(END, "\tSpeed: {:03.2f}\n\n".format(self.speed))
for sourcename in sorted(self.sources):
value = "{:8}:\t".format(sourcename)
for i,level in enumerate(self.levels[sourcename]):
g = self.pc.display.get_mod_slot_label(i)+'%s '%self.pc.display.get_bar(level)
for i, level in enumerate(self.levels[sourcename]):
g = self.pc.display.get_mod_slot_label(i) + '%s ' % self.pc.display.get_bar(level)
value += g
value += "\t"
value += self.display_values.get(sourcename) or "{:4.2f}%".format(self.values.get(sourcename,0)*100) or "None"
display.display_text.insert(END,value + "\n")
value += self.display_values.get(sourcename) or "{:4.2f}%".format(self.values.get(sourcename, 0) * 100) or "None"
display.display_text.insert(END, value + "\n")
"""display.display_text.insert(END, "%s\n" %self.last_lfo_status[lfo])
display.display_text.insert(END, "\t%s\n" % self.formula[lfo])"""
#display.display_text.insert(END, "\nLevels:%s\n\n" % self.levels)
# 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 or self.stream is None:
return
data = np.fromstring(self.stream.read(self.CHUNK, exception_on_overflow = False),dtype=np.int16)
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)
self.values[sourcename] = value
if value is None:
if value is None:
continue
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])
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):
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)
print("\n>>>>>>Triggering dynamic change for meandiff %s?\n" % meandiff)
# TODO: add configurable triggering - eg trigger next preset, next shader, next video..
#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))
# 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 this value)
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 this value)
GAIN_MULT = 1.0
def energy(self,data):
peak=np.average(np.abs(data))*2
value = (peak/2**16)/16 * 100
def energy(self, data):
peak = np.average(np.abs(data)) * 2
value = (peak / 2 ** 16) / 16 * 100
value *= (self.GAIN_MULT * self.config['energy']['gain'])
value = value - self.config['energy']['threshold']
if value<0.0:
if value < 0.0:
value = 0.0
if value>1.0:
if value > 1.0:
value = 1.0
bars="#"*int(50*value)
if self.DEBUG: print("energy:\t\t%05d %s\t(converted to %s)"%(peak,bars,value))
bars = "#" * int(50 * value)
if self.DEBUG:
print("energy:\t\t%05d %s\t(converted to %s)" % (peak, bars, value))
self.display_values['energy'] = "{} gn:{} trsh:{} trg:{}".format(
self.pc.display.get_bar(value),
self.pc.display.get_bar(self.config['energy']['gain']),
self.pc.display.get_bar(self.config['energy']['threshold']),
self.pc.display.get_bar(self.config['energy'].setdefault('triggerthreshold',0.15))
)
return value
# dont think this works properly, or maybe it do just be like that
def peakfreq(self,data):
data = data.copy() * np.hanning(len(data)) # smooth the FFT by windowing data
fft = abs(np.fft.fft(data).real)
fft = fft[:int(len(fft)/2)] # keep only first half
freq = np.fft.fftfreq(self.CHUNK,1.0/self.RATE)
freq = freq[:int(len(freq)/2)] # keep only first half
freqPeak = freq[np.where(fft==np.max(fft))[0][0]]+1
if freqPeak<400:
return False
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)
self.pc.display.get_bar(value),
self.pc.display.get_bar(self.config['energy']['gain']),
self.pc.display.get_bar(self.config['energy']['threshold']),
self.pc.display.get_bar(self.config['energy'].setdefault('triggerthreshold', 0.15))
)
return value
# dont think this works properly, or maybe it do just be like that
def peakfreq(self, data):
data = data.copy() * np.hanning(len(data)) # smooth the FFT by windowing data
fft = abs(np.fft.fft(data).real)
fft = fft[:int(len(fft) / 2)] # keep only first half
freq = np.fft.fftfreq(self.CHUNK, 1.0 / self.RATE)
freq = freq[:int(len(freq) / 2)] # keep only first half
freqPeak = freq[np.where(fft == np.max(fft))[0][0]] + 1
if freqPeak < 400:
return False
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)
return value
# ActionsPlugin methods
@property
def parserlist(self):
return [
( r"^toggle_sound_react_active$", self.toggle_active ),
( r"^sound_set_config_([a-z]*)_([a-z]*)$", self.set_config ),
( r"^sound_set_modulation_([a-z]*)_slot_([0-3])_level$", self.set_modulation_source_slot_level ),
(r"^toggle_sound_react_active$", self.toggle_active),
(r"^sound_set_config_([a-z]*)_([a-z]*)$", self.set_config),
(r"^sound_set_modulation_([a-z]*)_slot_([0-3])_level$", self.set_modulation_source_slot_level),
]
def set_modulation_source_slot_level(self, sourcename, slot, level):
self.levels.setdefault(sourcename,[0.0,0.0,0.0,0.0])[slot] = level
self.levels.setdefault(sourcename, [0.0, 0.0, 0.0, 0.0])[slot] = level
def set_config(self, sourcename, setting, value):
if type(self.config.get(sourcename,{}).get(setting)) is str:
print ("SoundReactPlugin: type of existing setting is string, probably doesnt make sense to set this to a value of this type!")
if type(self.config.get(sourcename, {}).get(setting)) is str:
print("SoundReactPlugin: type of existing setting is string, probably doesnt make sense to set this to a value of this type!")
self.config[sourcename][setting] = value
def toggle_active(self):

View File

@@ -1,7 +1,7 @@
import data_centre.plugin_collection
from data_centre.plugin_collection import ActionsPlugin, SequencePlugin
class TestPlugin(ActionsPlugin,SequencePlugin):
class TestPlugin(ActionsPlugin, SequencePlugin):
disabled = True
def __init__(self, plugin_collection):
@@ -9,39 +9,41 @@ class TestPlugin(ActionsPlugin,SequencePlugin):
@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 ),
( r"^print_arguments$", self.print_arguments ),
( r"^set_the_shader_param_([0-3])_layer_offset_([0-2])_continuous_inverted_example$", self.invert_shader_param_layer )
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),
(r"^print_arguments$", self.print_arguments),
(r"^set_the_shader_param_([0-3])_layer_offset_([0-2])_continuous_inverted_example$", self.invert_shader_param_layer)
]
def test_plugin(self):
print ("TEST PLUGIN test_plugin CALLED!!")
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:
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):
for i, shader in enumerate(self.pc.message_handler.shaders.selected_shader_list):
self.pc.actions.call_method_name(
"play_shader_%s_%s" % (i, self.cycle_count), None
"play_shader_%s_%s" % (i, self.cycle_count), None
)
self.pc.actions.call_method_name(
"start_shader_layer_%s" % i, None
"start_shader_layer_%s" % i, None
)
self.cycle_count += 1
duration = 5000
frequency = 50
def run_sequence(self, position):
self.pc.actions.call_method_name(
"set_the_shader_param_3_layer_0_continuous", position
@@ -57,7 +59,7 @@ class TestPlugin(ActionsPlugin,SequencePlugin):
def invert_shader_param_layer(self, param, layer, value):
# invert the value
self.pc.actions.call_method_name(
"set_the_shader_param_%s_layer_offset_%s_continuous" % ( param, layer), 1.0 - value
"set_the_shader_param_%s_layer_offset_%s_continuous" % (param, layer), 1.0 - value
# if you were calling an action with no argument, use eg:
# "toggle_automation_pause", None
)

View File

@@ -1,13 +1,15 @@
import serial
from serial import Serial
import data_centre.plugin_collection
from data_centre.plugin_collection import ActionsPlugin, SequencePlugin, DisplayPlugin, ModulationReceiverPlugin, AutomationSourcePlugin
import threading
import time
import serial
from data_centre.plugin_collection import ActionsPlugin, AutomationSourcePlugin, DisplayPlugin, ModulationReceiverPlugin, SequencePlugin
class AsyncWriter(threading.Thread):
queue = []
quit_flag = False
def __init__(self, plugin):
super().__init__()
self.plugin = plugin
@@ -16,38 +18,38 @@ class AsyncWriter(threading.Thread):
self.queue.append(data)
def ready(self):
return len(self.queue)>0
return len(self.queue) > 0
def quit(self):
self.quit_flag = True
def run(self):
while not self.quit_flag:
#print("AsyncWriter looping..")
# print("AsyncWriter looping..")
if not self.plugin.active or self.plugin.disabled:
#print("plugin active or disabled - exiting!")
# print("plugin active or disabled - exiting!")
return
if self.plugin.ser is None or not self.plugin.ser:
#print("no stream - skipping")
# print("no stream - skipping")
time.sleep(0.5)
continue
if not self.ready():
#print("not ready - skipping")
# print("not ready - skipping")
time.sleep(0.005)
continue
item = self.queue.pop(0)
if item is not None:
#print("sending %s" % item)
# print("sending %s" % item)
self.plugin.ser.write(item)
time.sleep(len(item)*0.005)
time.sleep(len(item) * 0.005)
else:
time.sleep(0.01)
if len(self.queue)>4:
if len(self.queue) > 4:
self.queue = self.queue[-4:4]
class WJSendPlugin(ActionsPlugin, SequencePlugin, DisplayPlugin, ModulationReceiverPlugin, AutomationSourcePlugin):
DEBUG = False #True
DEBUG = False # True
ser = None
active = True
@@ -67,7 +69,7 @@ class WJSendPlugin(ActionsPlugin, SequencePlugin, DisplayPlugin, ModulationRecei
rtscts=True, # TODO : test without this one
timeout=timeout)"""
THROTTLE = 20 # milliseconds to wait between refreshing parameters
THROTTLE = 20 # milliseconds to wait between refreshing parameters
selected_command_name = None
selected_argument_index = 0
@@ -82,24 +84,24 @@ class WJSendPlugin(ActionsPlugin, SequencePlugin, DisplayPlugin, ModulationRecei
self.presets = self.load_presets()
print("read presets:\n%s\n" % self.presets)
# load the stored modulation levels into the current config
for cmd,levels in self.presets['modulation_levels'].items():
for cmd, levels in self.presets['modulation_levels'].items():
self.commands[cmd]['modulation'] = levels.copy()
# build a reverse map of friendly name -> command struct for later use
for cmd,struct in self.commands.items():
for cmd, struct in self.commands.items():
self.command_by_queue[struct['queue']] = struct
self.pc.actions.tk.after(500, self.start_plugin)
self.selected_command_name = list(sorted(self.commands.keys()))[0] # select first command
self.selected_command_name = list(sorted(self.commands.keys()))[0] # select first command
def load_presets(self):
print("trying load presets? %s " % self.PRESET_FILE_NAME)
return self.pc.read_json(self.PRESET_FILE_NAME) or { 'modulation_levels': {} }
return self.pc.read_json(self.PRESET_FILE_NAME) or {'modulation_levels': {}}
def save_presets(self):
for cmd,struct in self.commands.items():
self.presets.setdefault('modulation_levels',{})[cmd] = struct.get('modulation',[{},{},{},{}])
for cmd, struct in self.commands.items():
self.presets.setdefault('modulation_levels', {})[cmd] = struct.get('modulation', [{}, {}, {}, {}])
self.pc.update_json(self.PRESET_FILE_NAME, self.presets)
def start_plugin(self):
@@ -112,14 +114,14 @@ class WJSendPlugin(ActionsPlugin, SequencePlugin, DisplayPlugin, ModulationRecei
self.asyncwriter = None
self.save_presets()
# methods/vars for AutomationSourcePlugin
# a lot of the nitty-gritty handled in parent class, these are for interfacing to the plugin
last_record = {}
def get_frame_data(self):
diff = self.last_record.copy()
#self.last_record = {}
#print(">>> reporting frame data for rec\n\t%s" % diff)
# self.last_record = {}
# print(">>> reporting frame data for rec\n\t%s" % diff)
return diff
def recall_frame_data(self, data):
@@ -128,7 +130,7 @@ class WJSendPlugin(ActionsPlugin, SequencePlugin, DisplayPlugin, ModulationRecei
# print(">>>>recall from data:\n\t%s\n" %data)
for queue, item in data.items():
if item is not None:
self.send_buffered(queue, item[0], item[1], record = False)
self.send_buffered(queue, item[0], item[1], record=False)
def get_frame_summary(self, data):
line = "WJMX: "
@@ -140,76 +142,79 @@ class WJSendPlugin(ActionsPlugin, SequencePlugin, DisplayPlugin, ModulationRecei
# methods for ModulationReceiverPlugin - receives changes to the in-built modulation levels (-1 to +1)
# experimental & hardcoded !
# TODO: make this not hardcoded and configurable mapping modulation to parameters, preferably on-the-fly..
modulation_value = [0.0,0.0,0.0,0.0]
modulation_value = [0.0, 0.0, 0.0, 0.0]
def set_modulation_value(self, param, value):
self.modulation_value[param] = value ## invert so that no signal always gives a value ..
#print("storing modulation slot %s as %s" % (param,value))
self.modulation_value[param] = value ## invert so that no signal always gives a value ..
# print("storing modulation slot %s as %s" % (param,value))
# take modulation value and throw it to local parameter
if self.DEBUG: print("||||| WJSendPlugin received set_modulation_value for param %s with value %s!" % (param, value))
#v = (0.5+value)/2
if self.DEBUG:
print("||||| WJSendPlugin received set_modulation_value for param %s with value %s!" % (param, value))
# v = (0.5+value)/2
"""mapped = [
'mix',
'colour_T',
#'colour_T',
'back_colour:x'
]"""
#self.catch_all(*mapped[param].split(":")+[v])
#self.commands[
# self.catch_all(*mapped[param].split(":")+[v])
# self.commands[
# find which commands are mapped to this modulation, and trigger a send of them
# so that they update with the new modulation value
to_send = {}
for queue,cmd in sorted(self.command_by_queue.items(),reverse=True):
cmd.setdefault('modulation',[{},{},{},{}])
for queue, cmd in sorted(self.command_by_queue.items(), reverse=True):
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)
to_send[cmd['queue']] = cmd
continue
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)
to_send[cmd['queue']] = cmd
continue
for queue,cmd in sorted(to_send.items(),reverse=True):
self.send_buffered(cmd['queue'], cmd['form'], [x for x in [ cmd['arguments'][y] for y in cmd['arg_names'] ] ], record=False)
#with self.queue_lock:
#self.send(cmd['queue'], cmd['form'], [x for x in [ cmd['arguments'][y] for y in cmd['arg_names'] ] ])
#methods for DisplayPlugin
for queue, cmd in sorted(to_send.items(), reverse=True):
self.send_buffered(cmd['queue'], cmd['form'], [x for x in [cmd['arguments'][y] for y in cmd['arg_names']]], record=False)
# with self.queue_lock:
# self.send(cmd['queue'], cmd['form'], [x for x in [ cmd['arguments'][y] for y in cmd['arg_names'] ] ])
# methods for DisplayPlugin
def show_plugin(self, display, display_mode):
from tkinter import Text, END
from tkinter import END
display.display_text.insert(END, '{} \n'.format(display.body_title))
display.display_text.insert(END, "WJSendPlugin {}\n\n".format('ACTIVE' if self.active else 'not active'))
for queue, last in sorted(self.last_modulated.items()):
is_selected = queue == self.commands[self.selected_command_name].get('queue')
indicator = " " if not is_selected else "<"
display.display_text.insert(END, "%s%s:\t%s\t%s" % (indicator,queue,self.last.get(queue)[1],self.last_modulated.get(queue)[1]))
display.display_text.insert(END, "%s%s:\t%s\t%s" % (indicator, queue, self.last.get(queue)[1], self.last_modulated.get(queue)[1]))
if is_selected:
display.display_text.insert(END, ">") # add indicator of the selected queue for param jobbies
display.display_text.insert(END, ">") # add indicator of the selected queue for param jobbies
display.display_text.insert(END, "\n")
cmd = self.commands[self.selected_command_name]
output = "\n" + "%s %s : %s\n" % (self.commands[self.selected_command_name].get('queue'), self.selected_command_name, cmd['name'])
output = "\n" + "%s %s : %s\n" % (self.commands[self.selected_command_name].get('queue'), self.selected_command_name, cmd['name'])
for arg_name in cmd['arg_names']:
is_selected = cmd['arg_names'].index(arg_name)==self.selected_argument_index
output += "\t " #Mod
is_selected = cmd['arg_names'].index(arg_name) == self.selected_argument_index
output += "\t " # Mod
indicator = " " if not is_selected else "["
output += "%s%s: "%(indicator,arg_name)
for slot,mods in enumerate(cmd.setdefault('modulation',[{},{},{},{}])):
#if arg_name in mods:
v = mods.get(arg_name,0.0)
g = '%s'%self.pc.display.get_bar(v)
output += "{}:{}|".format(self.pc.display.get_mod_slot_label(slot),g)
output += "%s%s: " % (indicator, arg_name)
for slot, mods in enumerate(cmd.setdefault('modulation', [{}, {}, {}, {}])):
# if arg_name in mods:
v = mods.get(arg_name, 0.0)
g = '%s' % self.pc.display.get_bar(v)
output += "{}:{}|".format(self.pc.display.get_mod_slot_label(slot), g)
if is_selected:
output+="]"
output += "]"
output += "\n"
display.display_text.insert(END, output+"\n")
display.display_text.insert(END, output + "\n")
def get_display_modes(self):
return ["WJMXSEND","NAV_WJMX"]
return ["WJMXSEND", "NAV_WJMX"]
# methods for SerialPlugin (TODO: if this needs generalising out!) and serial command queueing
def open_serial(self, port='/dev/ttyUSB0', baudrate=9600):
@@ -219,39 +224,40 @@ class WJSendPlugin(ActionsPlugin, SequencePlugin, DisplayPlugin, ModulationRecei
return
try:
self.ser = serial.Serial(
port=port,
baudrate=baudrate,
bytesize=serial.SEVENBITS,
parity=serial.PARITY_ODD,
stopbits=serial.STOPBITS_ONE,
xonxoff=False,
rtscts=True, # TODO : test without this one
timeout=None #timeout
port=port,
baudrate=baudrate,
bytesize=serial.SEVENBITS,
parity=serial.PARITY_ODD,
stopbits=serial.STOPBITS_ONE,
xonxoff=False,
rtscts=True, # TODO : test without this one
timeout=None # timeout
)
except Exception as e:
print ("WJSendPlugin>> open_serial failed: " + str(type(e)))
print("WJSendPlugin>> open_serial failed: " + str(type(e)))
self.disabled = True
import traceback
traceback.print_exc()
import threading
serial_lock = threading.Lock()
def send_serial_string(self, string):
# TODO: thread this so can implement throttling and reduce bottleneck...
if not self.active:
return
try:
if self.DEBUG:
if self.DEBUG:
print("WJSendPlugin>> sending string %s " % string)
output = b'\2' + string.encode('ascii') + b'\3'
#with self.serial_lock:
#self.ser.write(b'\2\2\2\2\3\3\3\3')
output = b'\2' + string.encode('ascii') + b'\3'
# with self.serial_lock:
# self.ser.write(b'\2\2\2\2\3\3\3\3')
if self.asyncwriter is None:
self.asyncwriter = AsyncWriter(self)
self.asyncwriter.start()
self.asyncwriter.write(output)
#self.ser.write(output) #.encode())
# self.ser.write(output) #.encode())
# TODO: sleeping here seems to help serial response lag problem?
"""self.sleep = 0.2 #self.pc.get_variable('A')
#print ("got sleep %s" % self.sleep)
@@ -259,17 +265,18 @@ class WJSendPlugin(ActionsPlugin, SequencePlugin, DisplayPlugin, ModulationRecei
#print("using sleep %s" % self.sleep)
import time
time.sleep(self.sleep/10.0)"""
#yield from self.ser.drain()
if self.DEBUG:
print("send_serial_string: sent string '%s'" % output) #.encode('ascii'))
#if 'S' in string:
# yield from self.ser.drain()
if self.DEBUG:
print("send_serial_string: sent string '%s'" % output) # .encode('ascii'))
# if 'S' in string:
# self.get_device_status()
except Exception as e:
print("\t%s: send_serial_string failed for '%s'" % (e,string))
print("\t%s: send_serial_string failed for '%s'" % (e, string))
queue = {}
import threading
queue_lock = threading.Lock()
# send the queued commands to WJMX
def refresh(self):
if not self.ser or self.ser is None:
@@ -278,11 +285,11 @@ class WJSendPlugin(ActionsPlugin, SequencePlugin, DisplayPlugin, ModulationRecei
try:
# sorting the commands that are sent seems to fix jerk and lag that is otherwise pretty horrendous
with self.queue_lock:
for queue, command in sorted(self.queue.items()):
self.send_buffered(queue, command[0], command[1])
#self.queue.clear()
for queue, command in sorted(self.queue.items()):
self.send_buffered(queue, command[0], command[1])
# self.queue.clear()
except Exception:
print ("WJSendPlugin>>> !!! CAUGHT EXCEPTION running queue %s!!!" % queue)
print("WJSendPlugin>>> !!! CAUGHT EXCEPTION running queue %s!!!" % queue)
import traceback
print(traceback.format_exc())
finally:
@@ -293,61 +300,62 @@ class WJSendPlugin(ActionsPlugin, SequencePlugin, DisplayPlugin, ModulationRecei
self.pc.shaders.root.after(self.THROTTLE, self.refresh)
def send(self, queue, form, args):
#self.send_buffered(queue,output)
# self.send_buffered(queue,output)
with self.queue_lock:
self.queue[queue] = (form, args) #output
self.queue[queue] = (form, args) # output
last = {}
last_modulated = {}
def send_buffered(self, queue, form, args, record = True):
def send_buffered(self, queue, form, args, record=True):
# only send if new command is different to the last one we sent
mod_args = self.modulate_arguments(self.command_by_queue.get(queue), args)
if self.last_modulated.get(queue)!=(form,mod_args):
#print("WJSendPlugin>> send_buffered attempting to parse queue\t%s with form\t'%s' and args\t%s" % (queue, form, args))
if self.last_modulated.get(queue) != (form, mod_args):
# print("WJSendPlugin>> send_buffered attempting to parse queue\t%s with form\t'%s' and args\t%s" % (queue, form, args))
# TODO: actually output modulated version of args
output = form.format(*mod_args)
self.send_serial_string(output)
else:
if self.DEBUG: print("WJSendPlugin>> skipping sending %s %s as it is similar to what was previously sent" % (form,mod_args))
if self.DEBUG:
print("WJSendPlugin>> skipping sending %s %s as it is similar to what was previously sent" % (form, mod_args))
self.last[queue] = (form,args)
self.last_modulated[queue] = (form,mod_args)
if self.last[queue]!=(form,args) and record:
self.last_record[queue] = (form,args)
self.last[queue] = (form, args)
self.last_modulated[queue] = (form, mod_args)
if self.last[queue] != (form, args) and record:
self.last_record[queue] = (form, args)
else:
pass
#print("WJSendPlugin>> found no difference between:\n\t%s\n\t%s\n?" % (self.last_modulated.get(queue), (form,mod_args)))
# print("WJSendPlugin>> found no difference between:\n\t%s\n\t%s\n?" % (self.last_modulated.get(queue), (form,mod_args)))
def send_append(self, command, value):
# append value to the command as a hex value - for sending commands that aren't preprogrammed
self.send(command.split(':')[0], "{}{:02X}", [ command, int(255*value) ])
self.send(command.split(':')[0], "{}{:02X}", [command, int(255 * value)])
def send_append_pad(self, pad, command, value):
# append value, padded to length - for sending commands that aren't preprogrammed
self.send(command.split(':')[0], "{}{:0%iX}"%pad, [ command, int(255*value) ])
self.send(command.split(':')[0], "{}{:0%iX}" % pad, [command, int(255 * value)])
# methods for ActionPlugin - preprogrammed parameters
@property
def parserlist(self):
return [
( r"^open_serial$", self.open_serial ),
( r"^wj_send_serial:([0-9a-zA-Z:]*)$", self.send_serial_string ),
#( r"^wj_set_colour:([A|B|T])_([x|y])$", self.set_colour ),
#( r"^wj_set_back_colour:([x|y|z])$", self.set_back_colour ),
#( r"^wj_set_position:([N|L])_([x|y])$", self.set_position ),
#( r"^wj_set_mix$", self.set_mix ),
( r"^wj_set_modulation_([a-zA-Z_]*)[:]?([a-zA-Z_]*)_slot_([0-3])_level$", self.set_modulation_command_argument_level),
( r"^wj_set_current_modulation_slot_([0-3])_level$", self.set_current_modulation_level ),
( r"^wj_send_append_pad:([0-9]*)_([[:0-9a-zA-Z]*)$", self.send_append_pad ),
( r"^wj_send_append:([:0-9a-zA-Z]*)$", self.send_append ),
( r"^wj_set_([a-zA-Z_]*)[:]?([a-zA-Z_]*)$", self.catch_all ),
( 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_reset_modulation$", self.reset_modulation_levels ),
( r"^wj_toggle_active$", self.toggle_active )
(r"^open_serial$", self.open_serial),
(r"^wj_send_serial:([0-9a-zA-Z:]*)$", self.send_serial_string),
# ( r"^wj_set_colour:([A|B|T])_([x|y])$", self.set_colour ),
# ( r"^wj_set_back_colour:([x|y|z])$", self.set_back_colour ),
# ( r"^wj_set_position:([N|L])_([x|y])$", self.set_position ),
# ( r"^wj_set_mix$", self.set_mix ),
(r"^wj_set_modulation_([a-zA-Z_]*)[:]?([a-zA-Z_]*)_slot_([0-3])_level$", self.set_modulation_command_argument_level),
(r"^wj_set_current_modulation_slot_([0-3])_level$", self.set_current_modulation_level),
(r"^wj_send_append_pad:([0-9]*)_([[:0-9a-zA-Z]*)$", self.send_append_pad),
(r"^wj_send_append:([:0-9a-zA-Z]*)$", self.send_append),
(r"^wj_set_([a-zA-Z_]*)[:]?([a-zA-Z_]*)$", self.catch_all),
(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_reset_modulation$", self.reset_modulation_levels),
(r"^wj_toggle_active$", self.toggle_active)
]
def toggle_active(self):
@@ -356,29 +364,31 @@ class WJSendPlugin(ActionsPlugin, SequencePlugin, DisplayPlugin, ModulationRecei
self.asyncwriter = None
def reset_modulation_levels(self):
for cmd,struct in self.commands.items():
struct['modulation'] = [{},{},{},{}]
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'
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'
self.commands[command_name].setdefault('modulation',[{},{},{},{}])[slot][argument_name] = level
self.commands[command_name].setdefault('modulation', [{}, {}, {}, {}])[slot][argument_name] = level
def set_current_modulation_level(self, slot, level):
self.set_modulation_command_argument_level(self.selected_command_name, self.commands[self.selected_command_name]['arg_names'][self.selected_argument_index], slot, level)
def select_previous_command(self):
selected_command_index = list(sorted(self.commands.keys())).index(self.selected_command_name)-1
if selected_command_index<0:
selected_command_index = len(self.commands.keys())-1
selected_command_index = list(sorted(self.commands.keys())).index(self.selected_command_name) - 1
if selected_command_index < 0:
selected_command_index = len(self.commands.keys()) - 1
self.selected_command_name = sorted(list(self.commands.keys()))[selected_command_index]
self.selected_argument_index = 0
def select_next_command(self):
selected_command_index = list(sorted(self.commands.keys())).index(self.selected_command_name)+1
if selected_command_index>=len(self.commands.keys()):
selected_command_index = list(sorted(self.commands.keys())).index(self.selected_command_name) + 1
if selected_command_index >= len(self.commands.keys()):
selected_command_index = 0
self.selected_command_name = sorted(list(self.commands.keys()))[selected_command_index]
@@ -386,91 +396,94 @@ class WJSendPlugin(ActionsPlugin, SequencePlugin, DisplayPlugin, ModulationRecei
def select_previous_argument(self):
self.selected_argument_index -= 1
if self.selected_argument_index<0:
self.selected_argument_index = len(self.commands[self.selected_command_name]['arg_names'])-1
if self.selected_argument_index < 0:
self.selected_argument_index = len(self.commands[self.selected_command_name]['arg_names']) - 1
def select_next_argument(self):
self.selected_argument_index += 1
if self.selected_argument_index>=len(self.commands[self.selected_command_name]['arg_names']):
if self.selected_argument_index >= len(self.commands[self.selected_command_name]['arg_names']):
self.selected_argument_index = 0
def catch_all(self, param, argument_name, value):
#print ("got catch-all %s, %s, %s" % (param, argument_name, value))
#arguments = packed_arguments.split("_") + [ value ]
#print("commands looks like %s" % self.commands)
# print ("got catch-all %s, %s, %s" % (param, argument_name, value))
# arguments = packed_arguments.split("_") + [ value ]
# print("commands looks like %s" % self.commands)
msg = self.commands[param]
if len(msg['arg_names'])==1: argument_name = msg['arg_names'][0]
msg['arguments'][argument_name] = int(value*255) # = arguments
self.send(msg['queue'], msg['form'], [ msg['arguments'][p] for p in msg['arg_names'] ] )
if len(msg['arg_names']) == 1:
argument_name = msg['arg_names'][0]
msg['arguments'][argument_name] = int(value * 255) # = arguments
self.send(msg['queue'], msg['form'], [msg['arguments'][p] for p in msg['arg_names']])
def modulate_arguments(self, command, args):
args = args.copy()
#if self.DEBUG: print("modulate_arguments passed %s and\n\t%s" % (command,args))
# 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))
#for i,m in enumerate(modlevels.values()):
for arg_name,m in modlevels.items():
if m>0.0:
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))
# for i,m in enumerate(modlevels.values()):
for arg_name, m in modlevels.items():
if m > 0.0:
arg_index = command.get('arg_names').index(arg_name)
if self.DEBUG: print("\t\tupdating modulation slot %s, arg is %s\n\t with modlevel '%s' * modvalue '%s'" % (arg_index, args[arg_index], m, self.modulation_value[slot]))
if self.DEBUG:
print("\t\tupdating modulation slot %s, arg is %s\n\t with modlevel '%s' * modvalue '%s'" % (arg_index, args[arg_index], m, self.modulation_value[slot]))
# amount, value, level
newvalue = self.pc.shaders.get_modulation_value(
args[arg_index]/255.0,
self.modulation_value[slot],
args[arg_index] / 255.0,
self.modulation_value[slot],
m
)
if self.DEBUG: print("\t\tnewvalue is %s" %newvalue)
args[arg_index] = int(255*newvalue)
if self.DEBUG: print("modulate_arguments returning:\n\t%s" % args)
if self.DEBUG:
print("\t\tnewvalue is %s" % newvalue)
args[arg_index] = int(255 * newvalue)
if self.DEBUG:
print("modulate_arguments returning:\n\t%s" % args)
return args
# panasonic parameters are 8 bit so go 0-255, so 127 is default of centre value
commands = {
'colour_gain_T': {
'name': 'Colour Corrector gain (both)',
'queue': 'VCG',
'form': 'VCG:T{:02X}',
'arg_names': [ 'v' ],
'arguments': { 'v': 127 }
},
'colour_T': {
'name': 'Colour Corrector (both)',
'queue': 'VCC',
'form': 'VCC:T{:02X}{:02X}',
'arg_names': [ 'x', 'y' ],
'arguments': { 'x': 127, 'y': 127 },
},
'mix': {
'name': 'Mix/wipe',
'queue': 'VMM',
'form': 'VMM:{:02X}',
'arg_names': [ 'v' ],
'arguments': { 'v': 127 },
},
'back_colour': {
'name': 'Matte colour HSV',
'queue': 'VBM',
'form': 'VBM:{:02X}{:02X}{:02X}',
'arg_names': [ 'h', 's', 'v' ],
'arguments': { 'h': 127, 's': 127, 'v': 127 },
},
'position_N': {
'name': 'Positioner joystick XY',
'queue': 'VPS',
'form': 'VPS:N{:02X}{:02X}',
'arg_names': [ 'y', 'x' ],
'arguments': { 'y': 127, 'x': 127 }
},
#'dsk_slice': { ## cant seem to find the right control code for this?!
# 'name': 'Downstream Key Slice,Slope',
# 'queue': 'VDS',
# 'form': 'VDS:{:02X}{:01X}',
# 'arg_names': [ 'slice','slope' ],
# 'arguments': { 'slice': 127, 'slope': 8 },
# 'modulation': [ {}, { 'slice': 1.0 }, {}, {} ]
#}
'colour_gain_T': {
'name': 'Colour Corrector gain (both)',
'queue': 'VCG',
'form': 'VCG:T{:02X}',
'arg_names': ['v'],
'arguments': {'v': 127}
},
'colour_T': {
'name': 'Colour Corrector (both)',
'queue': 'VCC',
'form': 'VCC:T{:02X}{:02X}',
'arg_names': ['x', 'y'],
'arguments': {'x': 127, 'y': 127},
},
'mix': {
'name': 'Mix/wipe',
'queue': 'VMM',
'form': 'VMM:{:02X}',
'arg_names': ['v'],
'arguments': {'v': 127},
},
'back_colour': {
'name': 'Matte colour HSV',
'queue': 'VBM',
'form': 'VBM:{:02X}{:02X}{:02X}',
'arg_names': ['h', 's', 'v'],
'arguments': {'h': 127, 's': 127, 'v': 127},
},
'position_N': {
'name': 'Positioner joystick XY',
'queue': 'VPS',
'form': 'VPS:N{:02X}{:02X}',
'arg_names': ['y', 'x'],
'arguments': {'y': 127, 'x': 127}
},
# 'dsk_slice': { ## cant seem to find the right control code for this?!
# 'name': 'Downstream Key Slice,Slope',
# 'queue': 'VDS',
# 'form': 'VDS:{:02X}{:01X}',
# 'arg_names': [ 'slice','slope' ],
# 'arguments': { 'slice': 127, 'slope': 8 },
# 'modulation': [ {}, { 'slice': 1.0 }, {}, {} ]
# }
}
command_by_queue = {}

View File

@@ -1,57 +1,59 @@
import copy
import json
from json import JSONEncoder
def _default(self, obj):
if getattr(obj.__class__,'to_json'):
#return _default.default(obj.to_json())
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] }
f = {'shader_params': [[None] * 4, [None] * 4, [None] * 4]}
pc = None
DEBUG_FRAMES = False#True
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 }
return self.f # { 'f': self.f }
def get(self, key, default=None):
return self.f.get(key,default)
return self.f.get(key, default)
def has(self, key):
return self.get(key) is not None
def store_live(self):
frame = {
#'selected_shader_slots': [ shader.get('slot',None) for shader in self.pc.shaders.selected_shader_list ],
'selected_shader': copy.deepcopy(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,
'shader_modulation_levels': copy.deepcopy(self.pc.shaders.modulation_level)
# 'selected_shader_slots': [ shader.get('slot',None) for shader in self.pc.shaders.selected_shader_list ],
'selected_shader': copy.deepcopy(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,
'shader_modulation_levels': copy.deepcopy(self.pc.shaders.modulation_level)
}
#print("about to call get_plugin_frame_data")
# print("about to call get_plugin_frame_data")
frame.update(self.pc.fm.get_plugin_frame_data())
self.f = frame
#print("built frame: %s" % self.f)
# print("built frame: %s" % self.f)
return self
def store_copy(self, f):
#print("told to store_copy %s" % f)
# print("told to store_copy %s" % f)
if f is not None:
if f.get('f') is not None: #isinstance(f, Frame):
if f.get('f') is not None: # isinstance(f, Frame):
f = f.get('f')
return self.store_copy(f.get('f'))
self.f = f
@@ -62,12 +64,12 @@ class Frame:
def get_active_shader_names(self):
s = ""
if self.has('selected_shader_slots'):
return ['-']*3
return ['-'] * 3
if self.has('selected_shader'):
return [ shader['name'].strip() for shader in self.get('selected_shader') ]
return [ self.pc.data.shader_bank_data[layer][x].get('name').strip() if x is not None else '-'\
for layer,x in enumerate(self.get('selected_shader_slots',[None]*3))
]
return [shader['name'].strip() for shader in self.get('selected_shader')]
return [self.pc.data.shader_bank_data[layer][x].get('name').strip() if x is not None else '-' \
for layer, x in enumerate(self.get('selected_shader_slots', [None] * 3))
]
def get_frame_summary(self):
summary = []
@@ -75,30 +77,30 @@ class Frame:
# list the recorded shader info in compact format
names = self.get_active_shader_names()
for layer in range(0,3): # number of shader layers
for layer in range(0, 3): # number of shader layers
s = self.get_shader_layer_summary(layer)
summary.append(s)
# handle summarising the rest of the recorded shader info, two-to-a-line where possible
count = 0
line = ""
for key,d in sorted(self.f.items()):
#print ("get_frame_summary: checking %s value %s" % (key,d))
if type(d) is dict and len(d)==0: # skip empty dicts
for key, d in sorted(self.f.items()):
# print ("get_frame_summary: checking %s value %s" % (key,d))
if type(d) is dict and len(d) == 0: # skip empty dicts
continue
if key in ["selected_shader","layer_active_status","shader_params","shader_speeds","selected_shader_slots"]:
if key in ["selected_shader", "layer_active_status", "shader_params", "shader_speeds", "selected_shader_slots"]:
# skip these as dealt with below
pass
elif key in ['shader_modulation_levels']:
for layer in range(3):
o = ""
for slot in range(4):
sl = self.pc.display.get_mod_slot_label(slot)
o+= sl + "["
for param in range(4):
o += self.pc.display.get_bar(d[layer][param][slot])
o+= "] "
summary.append("Shader layer %s: %s"%(layer,o))
o = ""
for slot in range(4):
sl = self.pc.display.get_mod_slot_label(slot)
o += sl + "["
for param in range(4):
o += self.pc.display.get_bar(d[layer][param][slot])
o += "] "
summary.append("Shader layer %s: %s" % (layer, o))
elif self.pc.get_plugin_for_class_name(key) is not None:
summary.append(self.pc.get_plugin_for_class_name(key).get_frame_summary(d))
"""elif key in ["WJSendPlugin"]:
@@ -107,44 +109,42 @@ class Frame:
else:
line += "%s: %s" % (key, d)
count += 1
if count%2==1:
if count % 2 == 1:
line += "\t"
elif count>0 and count%2==0:
elif count > 0 and count % 2 == 0:
summary.append(line)
line = ""
if line != "":
summary.append(line)
# add 'not shown' items
if len(not_shown)>0:
if len(not_shown) > 0:
summary.append(','.join(not_shown.keys()))
return summary
def get_shader_layer_summary(self, layer):
s = "%s%s" % (layer, " " if layer != self.pc.data.shader_layer else ">")
s += "["
s += self.pc.display.get_compact_indicators([\
(i==self.get('selected_shader_slots',[-1]*3)[layer]) or\
(self.has('selected_shader') and self.pc.data.shader_bank_data[layer][i]['name'] == self.get('selected_shader')[layer]['name'])\
for i in range(10)\
])
s += "]"
s = "%s%s" % (layer, " " if layer != self.pc.data.shader_layer else ">")
s += "["
s += self.pc.display.get_compact_indicators([ \
(i == self.get('selected_shader_slots', [-1] * 3)[layer]) or \
(self.has('selected_shader') and self.pc.data.shader_bank_data[layer][i]['name'] == self.get('selected_shader')[layer]['name']) \
for i in range(10) \
])
s += "]"
if self.has('layer_active_status'):
s += " %s " % (self.get('layer_active_status',['-']*3)[layer])
if self.has('layer_active_status'):
s += " %s " % (self.get('layer_active_status', ['-'] * 3)[layer])
if self.get('selected_shader'):
s += "{:14.14}".format(self.get('selected_shader')[layer].get('name').replace('.frag','').strip())
if self.get('selected_shader'):
s += "{:14.14}".format(self.get('selected_shader')[layer].get('name').replace('.frag', '').strip())
s += " " + self.get_shader_param_summary(layer) + " "
if self.has('shader_speeds'):
s += self.pc.display.get_speed_indicator(self.get('shader_speeds',[0.0]*3)[layer])
return s
s += " " + self.get_shader_param_summary(layer) + " "
if self.has('shader_speeds'):
s += self.pc.display.get_speed_indicator(self.get('shader_speeds', [0.0] * 3)[layer])
return s
def get_shader_param_summary(self, layer):
if self.get('shader_params') is None:
@@ -166,17 +166,17 @@ class Frame:
self.recall_frame_params()
for layer in range(3):
if preset.has('selected_shader_slots'): # deprecated/compatibility
if preset.has('selected_shader_slots'): # deprecated/compatibility
self.pc.actions.call_method_name('play_shader_%s_%s' % (layer, preset.get('selected_shader_slots')[layer]))
elif preset.has('selected_shader') and preset.get('selected_shader')[layer] is not None:
# match selected shader to a slot and call that back if it exists
found = False
for slot,shader in enumerate(self.pc.data.shader_bank_data[layer]):
for slot, shader in enumerate(self.pc.data.shader_bank_data[layer]):
if shader['name'] == preset.get('selected_shader')[layer]['name']:
self.pc.actions.call_method_name('play_shader_%s_%s' % (layer, slot))
found = True
break
if not found: # otherwise fall back to loading it separately
if not found: # otherwise fall back to loading it separately
self.pc.shaders.selected_shader_list[self.pc.data.shader_layer] = preset.get('selected_shader')[layer].copy()
self.pc.shaders.load_selected_shader()
@@ -187,40 +187,40 @@ class Frame:
level = preset.get('shader_modulation_levels')[layer][param][slot]
self.pc.shaders.set_param_layer_slot_modulation_level(param, layer, slot, level)
for (layer, active) in enumerate(preset.get('layer_active_status',[])):
for (layer, active) in enumerate(preset.get('layer_active_status', [])):
# print ("got %s layer with status %s " % (layer,active))
if 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 recall_frame_params(self):
#print("recall_frame_params got: %s" % preset.get('shader_params'))
for (layer, param_list) in enumerate(self.get('shader_params',[])):
# print("recall_frame_params got: %s" % preset.get('shader_params'))
for (layer, param_list) in enumerate(self.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):
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)
# 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.has('feedback_active'):
self.pc.data.feedback_active = self.get('feedback_active',self.pc.data.feedback_active)
self.pc.data.feedback_active = self.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.has('x3_as_speed'):
self.pc.data.settings['shader']['X3_AS_SPEED']['value'] = self.get('x3_as_speed',self.pc.data.settings['shader']['X3_AS_SPEED']['value'])
self.pc.data.settings['shader']['X3_AS_SPEED']['value'] = self.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.get('shader_speeds',[])):
for (layer, speed) in enumerate(self.get('shader_speeds', [])):
if speed is not None:
self.pc.actions.call_method_name('set_shader_speed_layer_%s_amount' % layer, speed)
@@ -229,19 +229,19 @@ class Frame:
from data_centre.plugin_collection import AutomationSourcePlugin
for plugin in self.pc.get_plugins(AutomationSourcePlugin):
#print("recalling for plugin %s with data %s" % (plugin, self.get(plugin.frame_key)))
# print("recalling for plugin %s with data %s" % (plugin, self.get(plugin.frame_key)))
plugin.recall_frame_data(self.get(plugin.frame_key))
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.get('shader_params')):
for i2,p in enumerate(f2):
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.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'] = [[None] * 4, [None] * 4, [None] * 4]
f['shader_params'][i][i2] = p
if frame2.has('feedback_active'):
@@ -254,7 +254,7 @@ class Frame:
if 'shader_speeds' in frame2.f:
f['shader_speeds'] = frame2.get('shader_speeds')
else:
for i,s in enumerate(frame2.get('shader_speeds')):
for i, s in enumerate(frame2.get('shader_speeds')):
if s is not None:
f['shader_speeds'][i] = s
@@ -263,19 +263,21 @@ class Frame:
from data_centre.plugin_collection import AutomationSourcePlugin
for plugin in self.pc.get_plugins(AutomationSourcePlugin):
f[plugin.frame_key] = plugin.merge_data(f.get(plugin.frame_key),frame2.get(plugin.frame_key))
f[plugin.frame_key] = plugin.merge_data(f.get(plugin.frame_key), frame2.get(plugin.frame_key))
if self.DEBUG_FRAMES: print("merge_frames: got return\t%s" % f)
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()
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 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 'feedback_active' in ignored:
@@ -283,26 +285,28 @@ class Frame:
if 'x3_as_speed' in ignored:
f['x3_as_speed'] = None
if 'shader_speeds' in ignored and 'shader_speeds' in frame:
for i,s in enumerate(frame.get('shader_speeds')):
if ignored['shader_speeds'][i] is not None:
f['shader_speeds'][i] = None
for i, s in enumerate(frame.get('shader_speeds')):
if ignored['shader_speeds'][i] is not None:
f['shader_speeds'][i] = None
if 'strobe_amount' in ignored:
f['strobe_amount'] = None
from data_centre.plugin_collection import AutomationSourcePlugin
for plugin in self.pc.get_plugins(AutomationSourcePlugin):
if ignored.get(plugin.frame_key) is not None:
#print("ignoring for %s:\n\t%s\n" % (plugin.frame_key, ignored.get(plugin.frame_key)))
f[plugin.frame_key] = plugin.get_ignored_data(f.get(plugin.frame_key,{}),ignored.get(plugin.frame_key,{}))
# print("ignoring for %s:\n\t%s\n" % (plugin.frame_key, ignored.get(plugin.frame_key)))
f[plugin.frame_key] = plugin.get_ignored_data(f.get(plugin.frame_key, {}), ignored.get(plugin.frame_key, {}))
if self.DEBUG_FRAMES: print("get_frame_ignored: got return\t%s" % f)
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()
# 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 self.DEBUG_FRAMES:
print("is_frame_empty: got frame\t%s" % frame)
if self.has('feedback_active'):
return False
@@ -311,15 +315,15 @@ class Frame:
if self.has('strobe_amount'):
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:
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 self.has('shader_speeds'):
for i,f in enumerate(frame['shader_speeds']):
if f is not None:
return False
for i, f in enumerate(frame['shader_speeds']):
if f is not None:
return False
from data_centre.plugin_collection import AutomationSourcePlugin
for plugin in self.pc.get_plugins(AutomationSourcePlugin):
@@ -328,11 +332,12 @@ class Frame:
if not plugin.is_frame_data_empty(frame.get(plugin.frame_key)):
return False
if self.DEBUG_FRAMES: print("is_frame_empty: got return true")
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
# if not last_frame: return current_frame
current_frame = current_frame.f
last_frame = self.f
@@ -341,12 +346,13 @@ class Frame:
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):
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))
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']:
@@ -359,8 +365,8 @@ class Frame:
else:
x3_as_speed = None
speed_values = [None]*3
for layer,param in enumerate(current_frame.get('shader_speeds',[None]*3)):
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
@@ -380,21 +386,19 @@ class Frame:
plugin_data[plugin.frame_key] = plugin.get_frame_diff(last_frame, current_frame)
diff = {
'shader_params': param_values,
'feedback_active': feedback_active,
'x3_as_speed': x3_as_speed,
'shader_speeds': speed_values,
'strobe_amount': strobe_amount,
'shader_params': param_values,
'feedback_active': feedback_active,
'x3_as_speed': x3_as_speed,
'shader_speeds': speed_values,
'strobe_amount': strobe_amount,
}
diff.update(plugin_data)
if self.DEBUG_FRAMES: print("returning\t%s\n^^^^" % diff['shader_params'])
if self.DEBUG_FRAMES:
print("returning\t%s\n^^^^" % diff['shader_params'])
return Frame(self.pc).store_copy(diff)
class FrameManager:
pc = None
@@ -432,9 +436,9 @@ class FrameManager:
from data_centre.plugin_collection import AutomationSourcePlugin
for plugin in self.pc.get_plugins(AutomationSourcePlugin):
data[plugin.frame_key] = plugin.get_frame_data()
#plugin.clear_recorded_frame()
# plugin.clear_recorded_frame()
#print("get_plugin_frame_data looks like %s" % data)
# print("get_plugin_frame_data looks like %s" % data)
return data
def interpolate_clip(self, frames):
@@ -446,9 +450,9 @@ class FrameManager:
# else,
# store as last value
print("got pre-interpolated clip: %s" % [ f.f for f in frames if f is not None])
print("got pre-interpolated clip: %s" % [f.f for f in frames if f is not None])
last = [ [None]*4, [None]*4, [None]*4 ]
last = [[None] * 4, [None] * 4, [None] * 4]
"""for findex,frame in enumerate(frames):
if frame is None:
@@ -457,18 +461,18 @@ class FrameManager:
reproc_to = 0
def process(self, findex, frame):
for layer,params in enumerate(frame.get('shader_params',[])):
for param,value in enumerate(params):
for layer, params in enumerate(frame.get('shader_params', [])):
for param, value in enumerate(params):
if value is None and last[layer][param] is not None:
# find distance to when this value changes again
gap,future_value = self.get_distance_value_layer_param(frames,findex,layer,param)
if gap==0 or future_value==value: # if doesnt change again, do nothing
gap, future_value = self.get_distance_value_layer_param(frames, findex, layer, param)
if gap == 0 or future_value == value: # if doesnt change again, do nothing
continue
newvalue = self.interpolate(last[layer][param], future_value, gap)
params[param] = newvalue
print("findex %s: updating interpolated value to %s - should be between %s and %s over gap %s" % (findex, newvalue, last[layer][param], future_value, gap))
last[layer][param] = newvalue
#elif last[layer][param] is None:
# elif last[layer][param] is None:
# reproc_to = findex
elif value is not None:
last[layer][param] = value
@@ -476,13 +480,13 @@ class FrameManager:
from data_centre.plugin_collection import AutomationSourcePlugin
for plugin in self.pc.get_plugins(AutomationSourcePlugin):
plugin.process_interpolate_clip(frames)
for i in range(2):
for findex,frame in enumerate(frames):
if frame is None:
continue
process(self,findex,frame)
for i in range(2):
for findex, frame in enumerate(frames):
if frame is None:
continue
process(self, findex, frame)
"""for findex in range(reproc_to):
if frames[findex] is None:
@@ -490,23 +494,23 @@ class FrameManager:
process(self,findex,frames[findex])"""
print("got interpolated clip: %s" % [ f.f for f in frames if f is not None ])
print("got interpolated clip: %s" % [f.f for f in frames if f is not None])
def get_distance_value_layer_param(self, frames, findex, layer, param):
for i in range(1,len(frames)):
for i in range(1, len(frames)):
search_findex = i + findex
search_findex %= len(frames)
if frames[search_findex] is not None and frames[search_findex].get('shader_params',[ [None]*4, [None]*4, [None]*4 ])[layer][param] is not None:
if frames[search_findex] is not None and frames[search_findex].get('shader_params', [[None] * 4, [None] * 4, [None] * 4])[layer][param] is not None:
return i, frames[search_findex].get('shader_params')[layer][param]
return 0, None
def interpolate(self, value1, value2, total_steps):
diff = max(value1,value2)-min(value1,value2)
diff = max(value1, value2) - min(value1, value2)
sl = diff / total_steps
if value1>value2:
v = value1-sl
if value1 > value2:
v = value1 - sl
else:
v = value1+sl
v = value1 + sl
print("interpolate between\t%s and\t%s over\t%s steps, got sl\t%s and value\t%s" % (value1,value2,total_steps, sl, v))
print("interpolate between\t%s and\t%s over\t%s steps, got sl\t%s and value\t%s" % (value1, value2, total_steps, sl, v))
return v

View File

@@ -1,24 +1,21 @@
#!/usr/bin/python
import traceback
from tkinter import Tk, Frame
import sys
import tracemalloc
import argparse
import traceback
from tkinter import Frame, Tk
from pythonosc import udp_client
from actions import Actions
from data_centre.data import Data
from display_centre.display import Display
from display_centre.messages import MessageHandler
from user_input.analog_input import AnalogInput
from user_input.midi_input import MidiInput
from user_input.numpad_input import NumpadInput
from user_input.osc_input import OscInput
from user_input.midi_input import MidiInput
from user_input.analog_input import AnalogInput
from video_centre.video_driver import VideoDriver
#from video_centre.capture import Capture
from video_centre.shaders import Shaders
import data_centre
from video_centre.video_driver import VideoDriver
# create tk object
tk = Tk()
@@ -32,6 +29,7 @@ message_handler = MessageHandler()
data = Data(message_handler)
def setup_osc_client():
client_parser = argparse.ArgumentParser()
client_parser.add_argument("--ip", default="127.0.0.1", help="the ip")
@@ -41,10 +39,11 @@ def setup_osc_client():
return udp_client.SimpleUDPClient(client_args.ip, client_args.port)
osc_client = setup_osc_client()
# setup the video driver
video_driver = VideoDriver(tk, osc_client, message_handler, data)
#capture = Capture(tk, osc_client, message_handler, data)
# capture = Capture(tk, osc_client, message_handler, data)
shaders = Shaders(tk, osc_client, message_handler, data)
# setup the display
@@ -70,14 +69,12 @@ actions.toggle_x_autorepeat()
frame.pack()
tk.attributes("-fullscreen", True)
def handle_error(exc, val, tb):
print('traceback for error : {}'.format(traceback.format_exc()))
message_handler.set_message('ERROR', val, traceback.format_exc())
tk.report_callback_exception = handle_error
tk.mainloop()

9
requirements.txt Normal file
View File

@@ -0,0 +1,9 @@
adafruit-mcp3008
gitpython
mido
numpy
omxplayer-wrapper
picamera
pyaudio
pyserial
python-osc

View File

@@ -1,6 +1,6 @@
import Adafruit_GPIO.SPI as SPI
import Adafruit_MCP3008
class AnalogInput(object):
def __init__(self, root, message_handler, display, actions, data):
self.root = root
@@ -10,23 +10,22 @@ class AnalogInput(object):
self.data = data
self.analog_mappings = data.analog_mappings
self.analog_delay = 50
self.last_readings = [0,0,0,0,0,0,0,0]
self.last_readings = [0, 0, 0, 0, 0, 0, 0, 0]
self.analog_input = None
self.check_if_listening_enabled()
def check_if_listening_enabled(self):
if self.data.settings['user_input']['ANALOG_INPUT']['value'] == 'enabled':
if not self.analog_input:
try:
## note - using software spi for now although on the same pins as the hardware spi described below because hardware spi wasnt working with lcd display
#SPI_PORT = 1
#SPI_DEVICE = 2
#self.analog_input = Adafruit_MCP3008.MCP3008(spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE))
CLK = 21
# SPI_PORT = 1
# SPI_DEVICE = 2
# self.analog_input = Adafruit_MCP3008.MCP3008(spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE))
CLK = 21
MISO = 19
MOSI = 20
CS = 16
CS = 16
self.analog_input = Adafruit_MCP3008.MCP3008(clk=CLK, cs=CS, miso=MISO, mosi=MOSI)
except:
@@ -38,12 +37,12 @@ class AnalogInput(object):
def poll_analog_inputs(self):
if self.data.settings['user_input']['ANALOG_INPUT']['value'] == 'enabled':
for i in range(0,8):
for i in range(0, 8):
if str(i) in self.analog_mappings:
this_reading = self.analog_input.read_adc(i)
#print(str(this_reading))
# print(str(this_reading))
if abs(this_reading - self.last_readings[i]) > 10:
#print('the diff is {}'.format(this_reading - self.last_readings[i]))
# print('the diff is {}'.format(this_reading - self.last_readings[i]))
self.run_action_for_mapped_channel(i, this_reading)
self.last_readings[i] = this_reading
self.root.after(self.analog_delay, self.poll_analog_inputs)
@@ -70,14 +69,12 @@ class AnalogInput(object):
method_name = this_mapping[mode][0]
if channel_value is not None:
norm_channel_value = channel_value/1023
norm_channel_value = channel_value / 1023
else:
norm_channel_value = None
print('the action being called is {}'.format(method_name))
self.actions.call_method_name(method_name, norm_channel_value)
## not sure whether we want to update the screen in general; here - probably not most of the time ...
#if 'cc' not in message_name:
# self.display.refresh_display()
# if 'cc' not in message_name:
# self.display.refresh_display()

View File

@@ -1,7 +1,5 @@
import string
import datetime
import mido
import subprocess
class MidiInput(object):
def __init__(self, root, message_handler, display, actions, data):
@@ -20,10 +18,10 @@ class MidiInput(object):
self.try_open_port()
def try_open_port(self):
#self.data.midi_status = 'disconnected'
# self.data.midi_status = 'disconnected'
self.midi_setting = self.data.settings['user_input']['MIDI_INPUT']['value']
self.port_index = self.data.midi_port_index
#print('try open port : midi setting is {}'.format(midi_setting))
# print('try open port : midi setting is {}'.format(midi_setting))
if self.midi_setting == 'usb':
self.actions.stop_serial_port_process()
self.open_this_port_and_start_listening('20')
@@ -31,7 +29,7 @@ class MidiInput(object):
self.actions.create_serial_port_process()
self.open_this_port_and_start_listening('serial')
else:
self.actions.stop_serial_port_process()
self.actions.stop_serial_port_process()
self.data.midi_status = 'disconnected'
self.root.after(1000, self.try_open_port)
@@ -42,7 +40,7 @@ class MidiInput(object):
midi_devices = [s for s in midi_devices if port_phrase in s]
if midi_devices:
if self.data.midi_status == 'disconnected':
subport_index = self.port_index % len(midi_devices)
subport_index = self.port_index % len(midi_devices)
self.midi_device = mido.open_input(midi_devices[subport_index])
self.data.midi_status = 'connected'
self.data.midi_device_name = self.midi_device.name
@@ -50,7 +48,7 @@ class MidiInput(object):
self.midi_mappings = self.data.load_midi_mapping_for_device(self.midi_device.name.split(":")[0])
self.midi_output = self.find_output_plugin(midi_devices[subport_index])
if self.midi_output:
#self.midi_feedback_device = mido.open_output(midi_device_on_port[subport_index])
# self.midi_feedback_device = mido.open_output(midi_device_on_port[subport_index])
self.root.after(self.midi_delay, self.refresh_midi_feedback)
self.poll_midi_input()
elif self.data.midi_status == 'connected':
@@ -64,7 +62,7 @@ class MidiInput(object):
current_message_buffer = [i.dict() for i in self.midi_device.iter_pending()]
refined_buffer = []
#refine buffer from lastest messages first
# refine buffer from lastest messages first
current_message_buffer.reverse()
for message in current_message_buffer:
# discard notes from wrong channel
@@ -83,7 +81,7 @@ class MidiInput(object):
for message in refined_buffer:
self.on_midi_message(message)
#print('the number processed {}'.format(i))
# print('the number processed {}'.format(i))
if self.data.settings['user_input']['MIDI_INPUT']['value'] == self.midi_setting and self.data.midi_port_index == self.port_index:
self.root.after(self.midi_delay, self.poll_midi_input)
else:
@@ -96,9 +94,9 @@ class MidiInput(object):
mapped_message_name = message_dict['type']
mapped_message_value = None
if 'note' in message_dict:
mapped_message_name = '{} {}'.format(mapped_message_name,message_dict['note'])
mapped_message_name = '{} {}'.format(mapped_message_name, message_dict['note'])
if 'control' in message_dict:
mapped_message_name = '{} {}'.format(mapped_message_name,message_dict['control'])
mapped_message_name = '{} {}'.format(mapped_message_name, message_dict['control'])
mapped_message_value = message_dict['value']
if mapped_message_name in self.midi_mappings.keys():
@@ -121,23 +119,23 @@ class MidiInput(object):
if self.data.function_on and len(this_mapping[mode]) > 1:
method_name = this_mapping[mode][1]
#self.data.function_on = False
# self.data.function_on = False
else:
method_name = this_mapping[mode][0]
#print('[][][][][ in mode {}, the action being called is {} from message_name {} in control_mode {}'
# print('[][][][][ in mode {}, the action being called is {} from message_name {} in control_mode {}'
# .format(mode, method_name, message_name, self.data.control_mode))
if mapped_message_value is not None:
norm_message_value = mapped_message_value/127
norm_message_value = mapped_message_value / 127
else:
norm_message_value = None
self.actions.call_method_name(method_name, norm_message_value)
## only update screen if not continuous - seeing if cc can respond faster if not refreshing screen on every action
if 'continuous' not in message_name:
self.display.refresh_display()
#self.refresh_midi_feedback()
# self.refresh_midi_feedback()
# Plugins to support MIDI feedback
@@ -145,31 +143,29 @@ class MidiInput(object):
# loop over the plugins
# find one that self.supports_midi_feedback(self.midi_device.name):
# open the midi device self.midi_feedback_device = mido.open_output(midi_device_on_port[subport_index])
print ("Looking for a MIDI Feedback plugin that supports %s..." % midi_device)
print("Looking for a MIDI Feedback plugin that supports %s..." % midi_device)
from data_centre.plugin_collection import MidiFeedbackPlugin
for p in self.data.plugins.get_plugins(MidiFeedbackPlugin):
if p.supports_midi_feedback(midi_device):
print ("Found one! Opening device")
print("Found one! Opening device")
p.set_midi_device(mido.open_output(midi_device))
return p
print ("Didn't find one!")
print("Didn't find one!")
def refresh_midi_feedback(self):
self.midi_output.refresh_midi_feedback()
if self.midi_output and self.data.settings['user_input']['MIDI_INPUT']['value'] == self.midi_setting and self.data.midi_port_index == self.port_index:
if self.midi_output.supports_midi_feedback(self.data.midi_device_name):
self.root.after(self.midi_delay*5, self.refresh_midi_feedback)
if self.midi_output.supports_midi_feedback(self.data.midi_device_name):
self.root.after(self.midi_delay * 5, self.refresh_midi_feedback)
def find_binding_for_action(self, action):
for bind,a in self.midi_mappings.items():
#print("looped over %s : %s " % (bind,a))
for (b,c) in a.items():
for bind, a in self.midi_mappings.items():
# print("looped over %s : %s " % (bind,a))
for (b, c) in a.items():
if action in c:
#print ("find_binding_for_action(%s) got %s" % (action, bind))
# print ("find_binding_for_action(%s) got %s" % (action, bind))
return bind

View File

@@ -1,5 +1,5 @@
import string
import sys
class NumpadInput(object):
KEY_000_DELAY = 100
@@ -23,11 +23,11 @@ class NumpadInput(object):
def on_key_press(self, event):
numpad = list(string.ascii_lowercase[0:19])
if event.char is 'h': # DISP button
#self.root.after(60, lambda:self.on_key_press_delay(event.char))
#return
if event.char is 'h': # DISP button
# self.root.after(60, lambda:self.on_key_press_delay(event.char))
# return
if self.data.is_display_held:
return # ignore spurious message if already held"""
return # ignore spurious message if already held"""
self.data.is_display_held = True
if self.in_0_event:
return
@@ -47,35 +47,35 @@ class NumpadInput(object):
print('{} is not in keypad map'.format(event.char))
def on_key_release(self, event):
numpad = list(string.ascii_lowercase[0:19])
if event.char in numpad:
self.check_key_release_settings(event.char)
numpad = list(string.ascii_lowercase[0:19])
if event.char in numpad:
self.check_key_release_settings(event.char)
##print ("--- releasing %s" % event.char)
# lag for 60ms to check that this is not a 'stream of 000' bullshit job from the keypad
if event.char is 'h' and not self.in_0_event:
self.root.after(self.KEY_000_DELAY+5, self.on_key_disp_release_delay)
#self.data.is_display_held = False
##print ("--- releasing %s" % event.char)
# lag for 60ms to check that this is not a 'stream of 000' bullshit job from the keypad
if event.char is 'h' and not self.in_0_event:
self.root.after(self.KEY_000_DELAY + 5, self.on_key_disp_release_delay)
# self.data.is_display_held = False
def on_mouse_move(self, event):
if self.data.settings['user_input'].setdefault(
'MOUSE_INPUT',
self.data.default_settings.get('MOUSE_INPUT',{'value': 'enabled'})).get('value') != 'enabled':
self.data.default_settings.get('MOUSE_INPUT', {'value': 'enabled'})).get('value') != 'enabled':
return
if event.x > 480 or event.y > 320:
return
width = 480
height = 320 # hard coded since display is fixed , and reading screen is more work
height = 320 # hard coded since display is fixed , and reading screen is more work
self.root.after(0, self.run_action_for_mapped_key, 'x_m', event.x / width)
self.root.after(0, self.run_action_for_mapped_key, 'y_m', event.y / height)
#self.run_action_for_mapped_key(event.char)
# self.run_action_for_mapped_key(event.char)
def select_display_mode_index(self, index):
if index >= len(self.data.get_display_modes_list()):
self.message_handler.set_message('ERROR', 'No page %s to display!' % index)
else:
self.actions.call_method_name("set_display_mode_%s"%self.data.get_display_modes_list()[index])
self.actions.call_method_name("set_display_mode_%s" % self.data.get_display_modes_list()[index])
def run_action_for_mapped_key(self, key, value=-1):
this_mapping = self.key_mappings[key]
@@ -101,7 +101,7 @@ class NumpadInput(object):
else:
print('the numpad action being called for \'{}\' is {} (mode is {})'.format(key, this_mapping[mode][is_function], mode))
if value != -1:
self.actions.call_method_name(this_mapping[mode][is_function],value)
self.actions.call_method_name(this_mapping[mode][is_function], value)
else:
self.actions.call_method_name(this_mapping[mode][is_function])
@@ -111,10 +111,8 @@ class NumpadInput(object):
if not value:
self.display.refresh_display()
def check_key_release_settings(self, key):
this_mapping = self.key_mappings[key]
if self.data.settings['sampler']['ACTION_GATED']['value'] == 'on':
if self.data.control_mode == 'PLAYER' and 'PLAYER' in this_mapping:
@@ -127,7 +125,7 @@ class NumpadInput(object):
self.run_action_for_mapped_key(key)
def on_key_disp_release_delay(self):
if not self.in_0_event:# and self.additional_0_in_event==0:
if not self.in_0_event: # and self.additional_0_in_event==0:
print("releasing !")
self.data.is_display_held = False
else:
@@ -140,36 +138,35 @@ class NumpadInput(object):
else:
print("ignoring press!")"""
def on_0_key_press(self) :
print ("on_0_key_press!")
if(not self.in_0_event ):
print (" first 0 received!")
self.in_0_event = True
def on_0_key_press(self):
print("on_0_key_press!")
if (not self.in_0_event):
print(" first 0 received!")
self.in_0_event = True
self.additional_0_in_event = 0
self.root.after(self.KEY_000_DELAY, self.check_event_outcome)
else:
print (" additional 0 received making %s!" % str(self.additional_0_in_event + 1))
self.additional_0_in_event = self.additional_0_in_event + 1
print(" additional 0 received making %s!" % str(self.additional_0_in_event + 1))
self.additional_0_in_event = self.additional_0_in_event + 1
def check_event_outcome(self):
if(self.additional_0_in_event == 0 ):
print (" no additional events, sending s")
self.in_0_event = False
if (self.additional_0_in_event == 0):
print(" no additional events, sending s")
self.in_0_event = False
self.run_action_for_mapped_key('s')
elif(self.additional_0_in_event > 1):
print (" %s additional events, sending n"%self.additional_0_in_event)
self.in_0_event = False
elif (self.additional_0_in_event > 1):
print(" %s additional events, sending n" % self.additional_0_in_event)
self.in_0_event = False
self.run_action_for_mapped_key('n')
elif(self.additional_0_in_event == 1):
elif (self.additional_0_in_event == 1):
print('this doesnt happen - may not be needed')
self.root.after(self.KEY_000_DELAY, self.second_check_event_outcome)
def second_check_event_outcome(self):
print("not supposed to happen?")
if(self.additional_0_in_event == 1 ):
self.in_0_event = False
if (self.additional_0_in_event == 1):
self.in_0_event = False
self.run_action_for_mapped_key('s')
elif(self.additional_0_in_event > 1):
self.in_0_event = False
elif (self.additional_0_in_event > 1):
self.in_0_event = False
self.run_action_for_mapped_key('n')

View File

@@ -1,12 +1,9 @@
import argparse
import string
import sys
import threading
from pythonosc import dispatcher
from pythonosc import dispatcher
from pythonosc import osc_server
import threading
import argparse
class OscInput(object):
@@ -17,14 +14,14 @@ class OscInput(object):
self.actions = actions
self.data = data
self.osc_mappings = data.osc_mappings
self.osc_enabled = False
self.osc_server = None
self.poll_settings_for_osc_info()
def poll_settings_for_osc_info(self):
self.data.settings['user_input'].setdefault('OSC_INPUT',
self.data.default_settings['user_input'].get('OSC_INPUT'))
self.data.default_settings['user_input'].get('OSC_INPUT'))
osc_setting_enabled = self.data.settings['user_input']['OSC_INPUT']['value'] == 'enabled'
if osc_setting_enabled and not self.osc_enabled:
self.setup_osc_server()
@@ -37,7 +34,7 @@ class OscInput(object):
def setup_osc_server(self):
ip_address = self.data.get_ip_for_osc_client()
if ip_address == 'none':
self.message_handler.set_message('INFO', 'osc failed - could not find ip')
self.message_handler.set_message('INFO', 'osc failed - could not find ip')
return
print('%%%%%%%%%%%%%%%%%%%%% setting up external_osc on ', ip_address)
server_parser = argparse.ArgumentParser()
@@ -47,15 +44,15 @@ class OscInput(object):
server_args = server_parser.parse_args()
this_dispatcher = dispatcher.Dispatcher()
# this_dispatcher.map("/keyboard/*", self.on_osc_input)
# this_dispatcher.map("/shaderparam0", self.on_param_osc_input)
# this_dispatcher.map("/shaderparam1", self.on_param_osc_input)
# this_dispatcher.map("/shaderparam2", self.on_param_osc_input)
# this_dispatcher.map("/shaderparam3", self.on_param_osc_input)
# this_dispatcher.map("/shutdown", self.exit_osc_server)
# this_dispatcher.map("/keyboard/*", self.on_osc_input)
# this_dispatcher.map("/shaderparam0", self.on_param_osc_input)
# this_dispatcher.map("/shaderparam1", self.on_param_osc_input)
# this_dispatcher.map("/shaderparam2", self.on_param_osc_input)
# this_dispatcher.map("/shaderparam3", self.on_param_osc_input)
# this_dispatcher.map("/shutdown", self.exit_osc_server)
this_dispatcher.map("/*", self.on_osc_input)
try:
osc_server.ThreadingOSCUDPServer.allow_reuse_address = True
server = osc_server.ThreadingOSCUDPServer((server_args.ip, server_args.port), this_dispatcher)
@@ -66,7 +63,6 @@ class OscInput(object):
except:
self.message_handler.set_message('INFO', 'failed to start osc listener')
def exit_osc_server(self, unused_addr, args):
print('%%%%%%%%%%%%%%%%%%%%% exiting external_osc')
try:
@@ -74,7 +70,6 @@ class OscInput(object):
self.message_handler.set_message('INFO', 'osc deactive')
except:
self.message_handler.set_message('INFO', 'osc shutdown failed')
def on_osc_input(self, addr, args):
if 'keyboard' in addr:
@@ -82,8 +77,7 @@ class OscInput(object):
elif 'shutdown' in addr:
self.exit_osc_server(addr, args)
else:
self.on_param_osc_input(addr,args)
self.on_param_osc_input(addr, args)
def on_key_osc_input(self, addr, args):
args = str(args)
@@ -101,7 +95,7 @@ class OscInput(object):
print("the address", addr)
self.run_action_for_osc_channel(addr, param_value=args)
def run_action_for_osc_channel(self, channel, param_value=None):
this_mapping = self.osc_mappings[channel]
if type(self.data.control_mode) is list:
@@ -114,16 +108,15 @@ class OscInput(object):
mode = self.data.control_mode
elif 'DEFAULT' in this_mapping:
mode = 'DEFAULT'
if self.data.function_on and len(this_mapping[mode]) > 1:
method_name = this_mapping[mode][1]
if self.data.settings['sampler']['FUNC_GATED']['value'] == 'off':
self.data.function_on = False
else:
method_name = this_mapping[mode][0]
self.actions.call_method_name(method_name, param_value)
if 'continuous' not in method_name:
self.display.refresh_display()

View File

@@ -1,23 +1,20 @@
def generate_mappings_doc(title, mappings, column_one_header="Note/CC"):
#print(mappings)
# print(mappings)
output = ""
output += "# %s\n" % title
output += "\n| %s | Mode | Action (default) | Action (with FN) | \n" % column_one_header
output += ("| --- " * 4) + " |\n"
for message, maps in sorted(mappings.items()):
#output += "| %s | " % message
# output += "| %s | " % message
for mode, actions in sorted(maps.items()):
output += "| "
output += "%s\t| " % message
output += "%s\t| " % mode
output += "%s | " % actions[0]
if len(actions)>1:
if len(actions) > 1:
output += "%s | " % actions[1]
output += "|\n"
output += "\n----\n"
print(output)

View File

@@ -1,5 +1,3 @@
class AltVideoPlayer:
def __init__(self, root, message_handler, data, osc_client, name):
self.root = root
@@ -18,15 +16,14 @@ class AltVideoPlayer:
self.load_attempts = 0
self.alpha = 0
self.show_toggle_on = True
### new stuff
### new stuff
self.client = osc_client
self.position = -1
def try_load(self, layer, is_current=False):
load_attempts = 0
while(load_attempts < 2):
while (load_attempts < 2):
load_attempts = load_attempts + 1
if self.load(layer, is_current):
print('load success')
@@ -36,7 +33,6 @@ class AltVideoPlayer:
self.message_handler.set_message('ERROR', 'failed to load')
self.status = 'ERROR'
return False
def load(self, layer, is_current=False):
self.get_context_for_player(is_current)
@@ -45,9 +41,9 @@ class AltVideoPlayer:
self.status = 'EMPTY'
return True
if(self.end is -1):
if (self.end is -1):
self.end = self.total_length
if(self.start is -1):
if (self.start is -1):
self.start = 0
self.client.send_message("/player/{}/load".format(self.name[0]), [self.location, self.start / self.total_length, self.end / self.total_length, self.rate])
self.crop_length = self.end - self.start
@@ -57,10 +53,10 @@ class AltVideoPlayer:
pass
self.set_alpha_value(0)
return True
#except (ValueError, SystemError) as e:
# print(e)
#self.message_handler.set_message('ERROR', 'load attempt fail')
#return False
# except (ValueError, SystemError) as e:
# print(e)
# self.message_handler.set_message('ERROR', 'load attempt fail')
# return False
def start_video(self):
if 'play' in self.data.settings['sampler']['ON_START']['value']:
@@ -73,9 +69,6 @@ class AltVideoPlayer:
else:
self.set_alpha_value(0)
def reload(self, layer, is_current=False):
self.exit()
self.player_running = False
@@ -131,7 +124,7 @@ class AltVideoPlayer:
new_rate = self.rate + amount
print('new rate is being set to {}'.format(new_rate))
if new_rate >= -3 and new_rate <= 3:
if new_rate >= -3 and new_rate <= 3:
self.client.send_message("/player/{}/speed".format(self.name[0]), new_rate)
self.rate = new_rate
return new_rate
@@ -149,15 +142,14 @@ class AltVideoPlayer:
self.root.after(100, self.exit)
def exit(self):
#self.last_player.exit()
# self.last_player.exit()
try:
self.client.send_message("/player/{}/quit".format(self.name[0]),True)
self.client.send_message("/player/{}/quit".format(self.name[0]), True)
self.player_running = False
except:
pass
## not sure if i am going to implement this atm
## not sure if i am going to implement this atm
def set_screen_size_for_dev_mode(self):
if self.data.settings['system']['DEV_MODE_RESET']['value'] == 'on':
##self.client.send_message("/player/{}/alpha".format(self.name[0]), amount)
@@ -165,17 +157,3 @@ class AltVideoPlayer:
else:
aspect_mode = self.data.settings['video']['SCREEN_MODE']['value']
return False, '--aspect-mode', aspect_mode

View File

@@ -1,8 +1,10 @@
import datetime
import fractions
import os
import subprocess
import datetime
import picamera
import fractions
class Capture(object):
PREVIEW_LAYER = 255
@@ -11,7 +13,7 @@ class Capture(object):
self.root = root
self.message_handler = message_handler
self.data = data
self.device = None
self.is_recording = False
self.is_previewing = False
@@ -21,16 +23,15 @@ class Capture(object):
## some capture settings
def create_capture_device(self):
if self.data.settings['capture']['TYPE']['value'] != 'usb':
if self.use_capture:
if self.piCapture_with_no_source():
return False
self.update_capture_settings()
try:
self.device = picamera.PiCamera(resolution=self.resolution, framerate=self.framerate, sensor_mode = self.sensor_mode)
self.device = picamera.PiCamera(resolution=self.resolution, framerate=self.framerate, sensor_mode=self.sensor_mode)
return True
except picamera.exc.PiCameraError as e:
self.use_capture = False
@@ -58,16 +59,15 @@ class Capture(object):
if self.capture_type == "piCaptureSd1":
self.sensor_mode = 6
else:
self.sensor_mode = 0
##update current instance (device) if in use
self.sensor_mode = 0
##update current instance (device) if in use
if self.device and not self.device.closed:
self.device.image_effect = self.data.settings['capture']['IMAGE_EFFECT']['value']
self.device.shutter_speed = self.convert_shutter_value(self.data.settings['capture']['SHUTTER']['value'])
## can only update resolution and framerate if not recording
## can only update resolution and framerate if not recording
if not self.is_recording:
self.device.framerate = self.framerate
self.device.resolution = self.resolution
def start_preview(self):
if self.use_capture == False:
@@ -83,7 +83,7 @@ class Capture(object):
self.set_preview_screen_size()
self.set_capture_settings()
self.device.preview.layer = self.PREVIEW_LAYER
return True
return True
def set_capture_settings(self):
if self.capture_type == "piCaptureSd1":
@@ -93,7 +93,6 @@ class Capture(object):
self.device.exposure_mode = "off"
else:
self.sensor_mode = 0
def set_preview_screen_size(self):
if self.data.settings['system']['DEV_MODE_RESET']['value'] == 'on':
@@ -119,7 +118,7 @@ class Capture(object):
is_created = self.create_capture_device()
if self.use_capture == False or not is_created:
return
if not os.path.exists(self.video_dir):
os.makedirs(self.video_dir)
self.is_recording = True
@@ -129,7 +128,7 @@ class Capture(object):
def monitor_disk_space(self):
if self.is_recording:
if self.check_available_disk_space():
self.root.after(10000, self.monitor_disk_space)
self.root.after(10000, self.monitor_disk_space)
else:
self.stop_recording()
@@ -143,9 +142,9 @@ class Capture(object):
def stop_recording(self):
self.device.stop_recording()
#set status to saving
# set status to saving
mp4box_process, recording_name = self.convert_raw_recording()
self.is_recording = 'saving'
self.is_recording = 'saving'
self.root.after(0, self.wait_for_recording_to_save, mp4box_process, recording_name)
self.update_capture_settings()
@@ -154,23 +153,22 @@ class Capture(object):
self.device.close()
def convert_raw_recording(self):
recording_path , recording_name = self.generate_recording_path()
recording_path, recording_name = self.generate_recording_path()
try:
if self.sensor_mode == 6:
mp4box_process = subprocess.Popen(['MP4Box', '-fps', '60', '-add', self.video_dir + '/raw.h264', recording_path])
return mp4box_process , recording_name
return mp4box_process, recording_name
else:
mp4box_process = subprocess.Popen(['MP4Box', '-add', self.video_dir + '/raw.h264', recording_path])
return mp4box_process , recording_name
return mp4box_process, recording_name
except Exception as e:
print(e)
if hasattr(e, 'message'):
error_info = e.message
else:
error_info = e
self.message_handler.set_message('ERROR',error_info)
self.message_handler.set_message('ERROR', error_info)
def wait_for_recording_to_save(self, process, name):
print('the poll is {}'.format(process.poll()))
if process.poll() is not None:
@@ -186,10 +184,10 @@ class Capture(object):
os.makedirs(rec_dir)
date = datetime.datetime.now().strftime("%Y-%m-%d")
i = 0
while os.path.exists('{}/rec-{}-{}.mp4'.format(rec_dir,date, i)):
while os.path.exists('{}/rec-{}-{}.mp4'.format(rec_dir, date, i)):
i += 1
name = 'rec-{}-{}.mp4'.format(date, i)
return '{}/{}'.format(rec_dir,name), name
return '{}/{}'.format(rec_dir, name), name
def get_recording_time(self):
if not self.device or not self.device.recording or self.device.frame.timestamp == None:
@@ -207,7 +205,7 @@ class Capture(object):
error_info = e.message
else:
error_info = e
self.message_handler.set_message('ERROR',error_info)
self.message_handler.set_message('ERROR', error_info)
return 0
return 0
@@ -249,8 +247,7 @@ class Capture(object):
def close_capture(self):
if self.device is not None:
print('closing the old camera...')
self.device.close()
self.device.close()
def receive_recording_finished(self, path, value):
pass

View File

@@ -1,17 +1,19 @@
import os
import subprocess
import datetime
import fractions
import picamera
import os
import subprocess
import time
import picamera
class OfCapture(object):
def __init__(self, root, osc_client, message_handler, data):
self.root = root
self.osc_client = osc_client
self.message_handler = message_handler
self.data = data
self.has_capture = False
self.is_recording = False
self.is_previewing = False
@@ -19,7 +21,7 @@ class OfCapture(object):
self.recording_start_time = 0
self.update_capture_settings()
self.of_recording_finished = True
#self.create_capture_device()
# self.create_capture_device()
def create_capture_device(self):
if self.use_capture and self.capture_type != 'usb':
@@ -29,13 +31,12 @@ class OfCapture(object):
self.update_capture_settings()
if not self.check_if_attached_with_picamera():
return
print('sending setup message !', self.capture_type)
self.osc_client.send_message("/capture/setup", self.capture_type)
self.has_capture = True
return True
def piCapture_with_no_source(self):
is_piCapture = subprocess.check_output(['pivideo', '--query', 'ready'])
if 'Video Processor was not found' not in str(is_piCapture):
@@ -55,12 +56,12 @@ class OfCapture(object):
if self.capture_type == "piCaptureSd1":
self.sensor_mode = 6
else:
self.sensor_mode = 0
self.sensor_mode = 0
def check_if_attached_with_picamera(self):
print('about to try open pcamera to check..')
try:
device = picamera.PiCamera(resolution=self.resolution, framerate=self.framerate, sensor_mode = self.sensor_mode)
device = picamera.PiCamera(resolution=self.resolution, framerate=self.framerate, sensor_mode=self.sensor_mode)
device.close()
return True
except picamera.exc.PiCameraError as e:
@@ -84,7 +85,7 @@ class OfCapture(object):
self.update_capture_settings()
self.osc_client.send_message("/capture/preview/start", True)
return True
def stop_preview(self):
self.osc_client.send_message("/capture/preview/stop", True)
self.is_previewing = False
@@ -101,20 +102,20 @@ class OfCapture(object):
if not self.check_available_disk_space():
return
if not os.path.exists(self.video_dir):
os.makedirs(self.video_dir)
self.is_recording = True
self.of_recording_finished = False
self.recording_start_time = time.time()
#self.device.start_recording(self.video_dir + '/raw.h264')
# self.device.start_recording(self.video_dir + '/raw.h264')
self.osc_client.send_message("/capture/record/start", True)
self.monitor_disk_space()
def monitor_disk_space(self):
if self.is_recording:
if self.check_available_disk_space():
self.root.after(10000, self.monitor_disk_space)
self.root.after(10000, self.monitor_disk_space)
else:
self.stop_recording()
@@ -127,22 +128,21 @@ class OfCapture(object):
return True
def stop_recording(self):
#self.device.stop_recording()
# self.device.stop_recording()
self.osc_client.send_message("/capture/record/stop", True)
self.is_recording = 'saving'
self.is_recording = 'saving'
self.wait_for_raw_file()
#set status to saving
# set status to saving
def wait_for_raw_file(self):
if os.path.exists(self.video_dir + 'raw.h264'):
mp4box_process, recording_name = self.convert_raw_recording()
self.root.after(0, self.wait_for_recording_to_save, mp4box_process, recording_name)
self.update_capture_settings()
elif os.path.exists(self.video_dir + 'raw.mp4') and self.of_recording_finished:
recording_path , recording_name = self.generate_recording_path()
os.rename(self.video_dir + 'raw.mp4', recording_path )
recording_path, recording_name = self.generate_recording_path()
os.rename(self.video_dir + 'raw.mp4', recording_path)
self.is_recording = False
self.data.create_new_slot_mapping_in_first_open(recording_name)
self.update_capture_settings()
@@ -152,22 +152,21 @@ class OfCapture(object):
def convert_raw_recording(self):
### wait for omx to finish creating video ...
if os.path.exists(self.video_dir + 'raw.h264'):
recording_path , recording_name = self.generate_recording_path()
framerate = 30 # hardcoded in openframeworks code for now
recording_path, recording_name = self.generate_recording_path()
framerate = 30 # hardcoded in openframeworks code for now
if self.capture_type == "piCaptureSd1":
framerate = framerate*2 # because picapture is interlaced
framerate = framerate * 2 # because picapture is interlaced
try:
mp4box_process = subprocess.Popen(['MP4Box', '-add', self.video_dir + 'raw.h264:fps={}'.format(framerate), recording_path])
return mp4box_process , recording_name
return mp4box_process, recording_name
except Exception as e:
print(e)
if hasattr(e, 'message'):
error_info = e.message
else:
error_info = e
self.message_handler.set_message('ERROR',error_info)
self.message_handler.set_message('ERROR', error_info)
def wait_for_recording_to_save(self, process, name):
print('the poll is {}'.format(process.poll()))
if process.poll() is not None:
@@ -183,10 +182,10 @@ class OfCapture(object):
os.makedirs(rec_dir)
date = datetime.datetime.now().strftime("%Y-%m-%d")
i = 0
while os.path.exists('{}/rec-{}-{}.mp4'.format(rec_dir,date, i)):
while os.path.exists('{}/rec-{}-{}.mp4'.format(rec_dir, date, i)):
i += 1
name = 'rec-{}-{}.mp4'.format(date, i)
return '{}/{}'.format(rec_dir,name), name
return '{}/{}'.format(rec_dir, name), name
def receive_recording_finished(self, unused_addr, position):
print('recieved recording finshed message !!!!')
@@ -194,9 +193,9 @@ class OfCapture(object):
def get_recording_time(self):
return time.time() - self.recording_start_time
#if not self.device or not self.device.recording or self.device.frame.timestamp == None:
# if not self.device or not self.device.recording or self.device.frame.timestamp == None:
# return -1
#else:
# else:
# return self.device.frame.timestamp / 1000000
def get_preview_alpha(self):
@@ -224,6 +223,3 @@ class OfCapture(object):
return int(1000000 / self.framerate)
else:
return int(fractions.Fraction(setting_value) * 1000000)

View File

@@ -1,11 +1,13 @@
import display_centre.menu as menu
import os
from statistics import mean
import display_centre.menu as menu
from data_centre.plugin_collection import ModulationReceiverPlugin
class Shaders(object):
MENU_HEIGHT = 10
EMPTY_SHADER = dict(name='none',is_shader=True,shad_type='-',param_number=4,path='-')
EMPTY_SHADER = dict(name='none', is_shader=True, shad_type='-', param_number=4, path='-')
MAX_MOD_SLOTS = 4
@@ -15,27 +17,27 @@ class Shaders(object):
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.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_param_list = [[0.0,0.0,0.0,0.0] for i in range(3)]
self.selected_status_list = ['-', '-', '-'] ## going to try using symbols for this : '-' means empty, '▶' means running, '■' means not running, '!' means error
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]
self.selected_modulation_slot = 0
#self.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] * self.MAX_MOD_SLOTS #,0.0,0.0,0.0]
# self.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] * self.MAX_MOD_SLOTS # ,0.0,0.0,0.0]
#self.load_selected_shader()
# self.load_selected_shader()
@property
def modulation_level(self):
return self.data.settings['shader'].setdefault('modulation_level',
[[[0.0,0.0,0.0,0.0] for i in range(4)] for i in range(3)])
return self.data.settings['shader'].setdefault('modulation_level',
[[[0.0, 0.0, 0.0, 0.0] for i in range(4)] for i in range(3)])
def set_modulation_levels(self, levels):
self.data.settings['shader']['modulation_level'] = levels
@@ -49,17 +51,16 @@ class Shaders(object):
has_path, path = self.get_path_for_shader(stripped_name)
shad_type = self.determine_shader_type(path)
parameter_number = self.determine_shader_parameter_number(path)
shaders_menu_list.append(dict(name=line['name'],is_shader=True,shad_type=shad_type,param_number=parameter_number,path=path))
else:
shaders_menu_list.append(dict(name=line['name'],is_shader=False,shad_type='',param_number=None,path=None))
return shaders_menu_list
shaders_menu_list.append(dict(name=line['name'], is_shader=True, shad_type=shad_type, param_number=parameter_number, path=path))
else:
shaders_menu_list.append(dict(name=line['name'], is_shader=False, shad_type='', param_number=None, path=None))
return shaders_menu_list
def get_path_for_shader(self, file_name):
######## returns full path for a given file name ########
for path in self.data.PATHS_TO_SHADERS:
for path in self.data.PATHS_TO_SHADERS:
for root, dirs, files in os.walk(path):
if file_name in files:
return True, '{}/{}'.format(root, file_name)
@@ -67,20 +68,19 @@ class Shaders(object):
def determine_shader_type(self, path):
with open(path, 'r', errors='ignore') as selected_shader:
shader_text = selected_shader.read()
if '//0-input' in shader_text:
return '0in'
elif '//1-input' in shader_text:
return '1in'
elif '//2-input' in shader_text:
return '2in'
else:
return '-'
shader_text = selected_shader.read()
if '//0-input' in shader_text:
return '0in'
elif '//1-input' in shader_text:
return '1in'
elif '//2-input' in shader_text:
return '2in'
else:
return '-'
def determine_shader_parameter_number(self, path):
max_amount = 4
if True: # for now always assume 4 params
if True: # for now always assume 4 params
return max_amount
with open(path, 'r') as selected_shader:
shader_text = selected_shader.read()
@@ -91,9 +91,9 @@ class Shaders(object):
def load_shader_layer(self, layer):
selected_shader = self.selected_shader_list[layer]
#self.selected_param_list[self.data.shader_layer] = [0.0,0.0,0.0,0.0]
# self.selected_param_list[self.data.shader_layer] = [0.0,0.0,0.0,0.0]
print("select shader: ", selected_shader)
self.osc_client.send_message("/shader/{}/load".format(str(layer)), [selected_shader['path'],selected_shader['shad_type'] == '2in',selected_shader['param_number']])
self.osc_client.send_message("/shader/{}/load".format(str(layer)), [selected_shader['path'], selected_shader['shad_type'] == '2in', selected_shader['param_number']])
if not self.selected_status_list[layer] == '':
self.selected_status_list[layer] = ''
@@ -119,7 +119,7 @@ class Shaders(object):
def map_on_shaders_selection(self):
index = self.shaders_menu.selected_list_index
is_file, name = self.shaders_menu.extract_file_type_and_name_from_menu_format(
self.shaders_menu_list[index]['name'])
self.shaders_menu_list[index]['name'])
if is_file:
is_successful = self.data.create_new_shader_mapping_in_first_open(name)
if not is_successful:
@@ -131,7 +131,7 @@ class Shaders(object):
selected_shader = self.selected_shader_list[self.data.shader_layer]
index = self.shaders_menu.selected_list_index
is_file, name = self.shaders_menu.extract_file_type_and_name_from_menu_format(
self.shaders_menu_list[index]['name'])
self.shaders_menu_list[index]['name'])
is_selected_shader = False
if is_file and name == selected_shader['name'].lstrip():
is_selected_shader = True
@@ -150,7 +150,7 @@ class Shaders(object):
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))
self.message_handler.set_message('INFO', "shader slot %s:%s is empty" % (layer, slot))
def play_this_shader(self, slot):
print(self.data.shader_bank_data[self.data.shader_layer])
@@ -159,15 +159,15 @@ class Shaders(object):
def increase_this_param(self, amount_change):
param = self.focused_param
current_amount = self.selected_param_list[self.data.shader_layer][param]
amount = self.get_new_param_amount(current_amount,amount_change)
amount = self.get_new_param_amount(current_amount, amount_change)
self.set_param_to_amount(param, amount)
def decrease_this_param(self, amount_change):
param = self.focused_param
current_amount = self.selected_param_list[self.data.shader_layer][param]
amount = self.get_new_param_amount(current_amount,-amount_change)
amount = self.get_new_param_amount(current_amount, -amount_change)
self.set_param_to_amount(param, amount)
def toggle_shader_speed(self):
if self.selected_speed_list[self.data.shader_layer] > 0.62:
self.set_speed_to_amount(0.5)
@@ -188,11 +188,11 @@ class Shaders(object):
def select_previous_shader_modulation_slot(self):
self.selected_modulation_slot -= 1
if self.selected_modulation_slot < 0:
self.selected_modulation_slot = self.MAX_MOD_SLOTS-1
self.selected_modulation_slot = self.MAX_MOD_SLOTS - 1
def reset_modulation(self, slot):
for layer in self.modulation_level:
for layer,levels in enumerate(layer):
for layer, levels in enumerate(layer):
levels[slot] = 0.0
def reset_selected_modulation(self):
@@ -204,13 +204,13 @@ class Shaders(object):
return 1
elif current + change < 0:
return 0
else:
else:
return current + change
def set_param_to_amount(self, param, amount, layer_offset=None):
start_layer = self.data.shader_layer
if self.data.settings['shader']['FIX_PARAM_OFFSET_LAYER']['value'] == 'enabled':
start_layer = 0
start_layer = 0
if layer_offset is None:
start_layer = self.data.shader_layer
layer_offset = 0
@@ -220,45 +220,47 @@ class Shaders(object):
def set_param_layer_to_amount(self, param, layer, amount):
if self.data.settings['shader']['X3_AS_SPEED']['value'] == 'enabled' and param == 3:
self.set_speed_layer_to_amount(layer, amount) #layer_offset=layer-self.data.shader_layer)
else:
self.set_speed_layer_to_amount(layer, amount) # layer_offset=layer-self.data.shader_layer)
else:
self.selected_param_list[layer][param] = amount
self.update_param_layer(param,layer)
self.update_param_layer(param, layer)
def get_modulation_value_list(self, amount, values, levels):
l = []
for i,v in enumerate(values):
for i, v in enumerate(values):
l.append(self.get_modulation_value(amount, v, levels[i]))
#print ("got mean %s from amount %s with %s*%s" % (mean(l), amount, values, levels))
# print ("got mean %s from amount %s with %s*%s" % (mean(l), amount, values, levels))
return mean(l)
def get_modulation_value(self, amount, value, level):
if level==0:
if level == 0:
return amount
# TODO: read from list of input formulas, from plugins etc to modulate the value
temp_amount = amount + (value * level)
#print("from amount %s, modulation is %s, temp_amount is %s" % (amount, modulation, temp_amount))
if temp_amount < 0: temp_amount = 0 # input range is 0-1 so convert back
if temp_amount > 1: temp_amount = 1 # modulation however is -1 to +1
# print("from amount %s, modulation is %s, temp_amount is %s" % (amount, modulation, temp_amount))
if temp_amount < 0:
temp_amount = 0 # input range is 0-1 so convert back
if temp_amount > 1:
temp_amount = 1 # modulation however is -1 to +1
return temp_amount
def send_param_layer_amount_message(self, param, layer, amount):
self.osc_client.send_message("/shader/{}/param".format(str(layer)), [param, amount] )
self.osc_client.send_message("/shader/{}/param".format(str(layer)), [param, amount])
def modulate_param_to_amount(self, param, value):
# incoming data here should be in format 0 to 1; needs changing to -1 to +1
self.modulation_value[param] = (value-0.5)*2 # normalise to -1 to +1
self.modulation_value[param] = (value - 0.5) * 2 # normalise to -1 to +1
for plugin in self.data.plugins.get_plugins(ModulationReceiverPlugin):
plugin.set_modulation_value(param, self.modulation_value[param])
for layer,params in enumerate(self.selected_param_list):
for ip,p in enumerate(params):
for p2,v in enumerate(self.modulation_level[layer][ip]):
if v!=0:
self.update_param_layer(ip,layer)
break
for layer, params in enumerate(self.selected_param_list):
for ip, p in enumerate(params):
for p2, v in enumerate(self.modulation_level[layer][ip]):
if v != 0:
self.update_param_layer(ip, layer)
break
def set_param_layer_offset_modulation_level(self, param, layer, level):
layer = (self.data.shader_layer + layer) % 3
@@ -275,12 +277,12 @@ class Shaders(object):
# merge all applicable layers
self.send_param_layer_amount_message(param, layer,
self.get_modulation_value_list(
self.selected_param_list[layer][param],
self.modulation_value,#[0], #param],
self.modulation_level[layer][param]
)
)
self.get_modulation_value_list(
self.selected_param_list[layer][param],
self.modulation_value, # [0], #param],
self.modulation_level[layer][param]
)
)
def set_speed_offset_to_amount(self, layer_offset, amount):
self.set_speed_to_amount(amount, layer_offset)
@@ -288,8 +290,7 @@ class Shaders(object):
def set_speed_to_amount(self, amount, layer_offset=0):
layer = (self.data.shader_layer + layer_offset) % 3
self.set_speed_layer_to_amount(layer, amount)
def set_speed_layer_to_amount(self, layer, amount):
self.osc_client.send_message("/shader/{}/speed".format(str(layer)), amount )
self.osc_client.send_message("/shader/{}/speed".format(str(layer)), amount)
self.selected_speed_list[layer] = amount

View File

@@ -1,8 +1,8 @@
from video_centre.video_player import VideoPlayer
from video_centre.alt_video_player import AltVideoPlayer
from video_centre.video_player import VideoPlayer
class VideoDriver(object):
MAX_LAYER = 254
def __init__(self, root, osc_client, message_handler, data):
@@ -15,28 +15,26 @@ class VideoDriver(object):
self.in_current_playing_cycle = False
self.in_next_load_cycle = False
self.in_parallel_cycle = False
self.layer = self.MAX_LAYER
self.last_player = None
self.current_player = None
self.next_player = None
self.reset_all_players()
self.update_video_settings()
if self.loop_parallel:
self.in_parallel_cycle = True
self.root.after(self.delay, self.begin_playing_parallel)
else:
self.root.after(self.delay, self.begin_playing)
#self.print_status()
# self.print_status()
def update_video_settings(self):
self.switch_on_finish = self.data.settings['sampler']['ON_FINISH']['value'] == 'switch'
self.loop_parallel = self.data.settings['sampler']['LOOP_TYPE']['value'] == 'parallel'
def get_next_layer_value(self):
if self.layer > 0:
self.layer = self.layer - 1
@@ -48,8 +46,8 @@ class VideoDriver(object):
def print_status(self):
print('self.loop_parallel: ', self.loop_parallel, 'self.in_parallel_cycle :', self.in_parallel_cycle)
print('l({}):{}, c({}):{}, n({}):{}'.format(self.last_player.name, self.last_player.status, self.current_player.name, \
self.current_player.status, self.next_player.name, self.next_player.status,))
self.root.after(1000,self.print_status)
self.current_player.status, self.next_player.name, self.next_player.status, ))
self.root.after(1000, self.print_status)
def begin_playing(self):
if self.current_player.try_load(self.get_next_layer_value()):
@@ -67,11 +65,11 @@ class VideoDriver(object):
self.root.after(self.delay, self.wait_for_first_load)
def switch_players_and_start_video(self):
self.in_first_load_cycle = False
self.in_current_playing_cycle = False
self.in_next_load_cycle = True
self.in_first_load_cycle = False
self.in_current_playing_cycle = False
self.in_next_load_cycle = True
self.switch_players()
self.switch_players()
def switch_players(self):
temp_player = self.last_player
@@ -83,7 +81,7 @@ class VideoDriver(object):
def start_video(self):
self.current_player.start_video()
self.last_player.exit()
self.last_player.exit()
self.next_player.try_load(self.get_next_layer_value())
self.in_current_playing_cycle = True
self.wait_for_next_cycle()
@@ -114,10 +112,9 @@ class VideoDriver(object):
else:
self.in_next_load_cycle = False
def get_player_info_for_status(self):
return self.current_player.bankslot_number, self.current_player.status, self.current_player.alpha, \
self.next_player.bankslot_number, self.next_player.status, self.next_player.alpha
self.next_player.bankslot_number, self.next_player.status, self.next_player.alpha
def get_player_info_for_banner(self, player):
if player == 'now':
@@ -129,20 +126,20 @@ class VideoDriver(object):
if self.next_player:
self.next_player.exit()
if self.current_player:
self.current_player.exit()
if self. last_player:
self.current_player.exit()
if self.last_player:
self.last_player.exit()
def reset_all_players(self):
self.exit_all_players()
state = self.data.settings['video']['VIDEOPLAYER_BACKEND']['value']
if(state == 'ofvideoplayer' or state == 'ofxomxplayer'):
if (state == 'ofvideoplayer' or state == 'ofxomxplayer'):
self.last_player = AltVideoPlayer(self.root, self.message_handler, self.data, self.osc_client, 'a.a')
self.current_player = AltVideoPlayer(self.root,self.message_handler, self.data, self.osc_client, 'b.b')
self.current_player = AltVideoPlayer(self.root, self.message_handler, self.data, self.osc_client, 'b.b')
self.next_player = AltVideoPlayer(self.root, self.message_handler, self.data, self.osc_client, 'c.c')
else:
self.last_player = VideoPlayer(self.root, self.message_handler, self.data, 'a.a')
self.current_player = VideoPlayer(self.root,self.message_handler, self.data, 'b.b')
self.current_player = VideoPlayer(self.root, self.message_handler, self.data, 'b.b')
self.next_player = VideoPlayer(self.root, self.message_handler, self.data, 'c.c')
def reload_next_player(self):
@@ -153,7 +150,7 @@ class VideoDriver(object):
def receive_position(self, unused_addr, player_name, args):
for player in [self.next_player, self.current_player, self.last_player]:
if player_name[0] in player.name :
if player_name[0] in player.name:
player.position = args * player.total_length
break

View File

@@ -1,5 +1,6 @@
from omxplayer.player import OMXPlayer
class VideoPlayer:
def __init__(self, root, message_handler, data, name):
self.root = root
@@ -20,10 +21,9 @@ class VideoPlayer:
self.alpha = 0
self.show_toggle_on = False
def try_load(self, layer, is_current=False):
load_attempts = 0
while(load_attempts < 2):
while (load_attempts < 2):
load_attempts = load_attempts + 1
if self.load(layer, is_current):
print('load success')
@@ -33,10 +33,9 @@ class VideoPlayer:
self.message_handler.set_message('ERROR', 'failed to load')
self.status = 'ERROR'
return False
def load(self, layer, is_current=False):
#try:
# try:
self.get_context_for_player(is_current)
is_dev_mode, first_screen_arg, second_screen_arg = self.set_screen_size_for_dev_mode()
arguments = ['--no-osd', '--layer', str(layer), '--adev', 'local', '--alpha', '0', first_screen_arg, second_screen_arg]
@@ -49,10 +48,10 @@ class VideoPlayer:
return True
self.omx_player = OMXPlayer(self.location, args=arguments, dbus_name=self.name)
self.omx_running = True
self.total_length = self.omx_player.duration() # <-- uneeded once self.duration stores float
if(self.end is -1):
self.total_length = self.omx_player.duration() # <-- uneeded once self.duration stores float
if (self.end is -1):
self.end = self.total_length
if(self.start is -1):
if (self.start is -1):
self.start = 0
self.crop_length = self.end - self.start
print('{}: the duration is {}'.format(self.name, self.total_length))
@@ -64,14 +63,14 @@ class VideoPlayer:
self.set_alpha_value(0)
self.pause_at_start()
return True
#except (ValueError, SystemError) as e:
# print(e)
#self.message_handler.set_message('ERROR', 'load attempt fail')
#return False
# except (ValueError, SystemError) as e:
# print(e)
# self.message_handler.set_message('ERROR', 'load attempt fail')
# return False
def pause_at_start(self):
position = self.get_position()
start_threshold = round(self.start - 0.02,2)
start_threshold = round(self.start - 0.02, 2)
if position > start_threshold:
if self.status == 'LOADING':
self.status = 'LOADED'
@@ -94,12 +93,12 @@ class VideoPlayer:
def pause_at_end(self):
position = self.get_position()
end_threshold = self.end - 0.2
if(position > end_threshold):
if (position > end_threshold):
self.status = 'FINISHED'
self.omx_player.pause()
print('its paused at end!')
elif(self.omx_running):
elif (self.omx_running):
self.root.after(5, self.pause_at_end)
def reload(self, layer, is_current=False):
@@ -123,7 +122,7 @@ class VideoPlayer:
def get_context_for_player(self, is_current=False):
next_context = self.data.get_next_context(is_current)
self.location = next_context['location']
#self.total_length = next_context['length']
# self.total_length = next_context['length']
self.start = next_context['start']
self.end = next_context['end']
self.bankslot_number = next_context['bankslot_number']
@@ -152,7 +151,7 @@ class VideoPlayer:
after_seek_position = position + amount
if after_seek_position > self.start and after_seek_position < self.end:
self.set_position(after_seek_position)
#self.player.seek(amount)
# self.player.seek(amount)
else:
self.message_handler.set_message('INFO', 'can not seek outside range')
@@ -161,7 +160,7 @@ class VideoPlayer:
if (new_rate > self.omx_player.minimum_rate() and new_rate < self.omx_player.maximum_rate()):
updated_speed = self.omx_player.set_rate(new_rate)
self.rate = new_rate
print('max rate {} , min rate {} '.format(self.omx_player.maximum_rate() ,self.omx_player.minimum_rate()))
print('max rate {} , min rate {} '.format(self.omx_player.maximum_rate(), self.omx_player.minimum_rate()))
return new_rate
else:
self.message_handler.set_message('INFO', 'can not set speed outside of range')
@@ -173,7 +172,7 @@ class VideoPlayer:
def exit(self):
try:
if self.omx_player:
print('trying to exit this player ', self.location)
print('trying to exit this player ', self.location)
self.omx_player.quit()
self.status = 'EMPTY'
self.omx_running = False
@@ -187,5 +186,3 @@ class VideoPlayer:
else:
aspect_mode = self.data.settings['video']['SCREEN_MODE']['value']
return False, '--aspect-mode', aspect_mode