-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathOSCBindingViewerComponent.py
157 lines (136 loc) · 7.26 KB
/
OSCBindingViewerComponent.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
from functools import partial
from _Framework.ControlSurfaceComponent import ControlSurfaceComponent
from _Framework.CompoundComponent import CompoundComponent
from _Framework.SubjectSlot import subject_slot
from ClyphX_Pro.clyphx_pro.MiscUtils import live_object_is_valid
from ClyphX_Pro.clyphx_pro.macrobat.ParameterRackBase import \
parameter_value_to_macro_value
from ClyphX_Pro.clyphx_pro.osc.OSCElements import OSCElement
from utils.log_utils import dumpobj
class BindingObserver(ControlSurfaceComponent):
"""
BindingObserver handles observing the name and value of the parameter bound
to a control and sending out OSC.
For the parameter name, the address is of the form: /control_name/name
For the parameter value, the address is of the form: /control_name/value
For the parameter value as an int, the address is of the form: /control_name/int
"""
def __init__(self, server, path, control, *a, **k):
super(BindingObserver, self).__init__(*a, **k)
self.is_private = True
self._server = server
self._path = path
self._control = control
self._on_parameter_changed.subject = control
self._on_parameter_changed(control.parameter)
def disconnect(self):
super(BindingObserver, self).disconnect()
self._server = None
self._path = None
self._control = None
@subject_slot('parameter')
def _on_parameter_changed(self, param):
subject = self._control if live_object_is_valid(param) else None
self._on_parameter_name_changed.subject = subject
self._on_parameter_value_changed.subject = subject
# using tasks here to thin out updates on parameter changes
self._tasks.add(
partial(self._on_parameter_name_changed, self._control.parameter_name))
self._tasks.add(
partial(self._on_parameter_value_changed, self._control.parameter_value))
track = self._control.binding_def.get('track') if subject else None
# self.canonical_parent.log_message(dumpobj(track))
track_name = track.name if track else None
track_color = track.color if track else None
# self.canonical_parent.log_message(dumpobj())
self._server.sendOSC('custom%s/track_name' % self._path, str(track_name))
self._server.sendOSC('custom%s/track_color' % self._path, str(track_color))
@subject_slot('parameter_name')
def _on_parameter_name_changed(self, name, _=None):
if not live_object_is_valid(self._control.parameter):
name = 'None'
# self._server.sendOSC('%s/name' % self._path, str(name))
self._server.sendOSC('custom%s/parameter_name' % self._path, str(name))
@subject_slot('parameter_value')
def _on_parameter_value_changed(self, value, _=None):
value_as_str = 'None'
value_as_int = 0
if live_object_is_valid(self._control.parameter):
# this is needed as some parameter values include special characters that
# can't be converted to strings. May want to add this to BindableElementMixin
# in the next bindings update.
value_as_str = ''.join(char for char in value if ord(char) < 128)
# might be good to add this to bindings too.
value_as_int = parameter_value_to_macro_value(self._control.parameter)
# self._server.sendOSC('%s/value' % self._path, str(value_as_str))
# self._server.sendOSC('%s/int' % self._path, int(value_as_int))
self._server.sendOSC('custom%s/value' % self._path, str(value_as_str))
self._server.sendOSC('custom%s/int' % self._path, int(value_as_int))
class OSCBindingViewerComponent(CompoundComponent):
"""
OSCBindingViewerComponent creates BindingObservers for all of the bound controls
of the first BindingComponent (the one owned by CXP). It also includes observers
and OSC elements for sending out the name of the selected track and device.
For track name, the address is /track/name
For device name, the address is /device/name
"""
def __init__(self, cx_core, *a, **k):
self.create_actions = lambda: None
super(OSCBindingViewerComponent, self).__init__(cx_core, *a, **k)
self.is_private = True
self._track_name_element = None
self._device_name_element = None
self.canonical_parent.schedule_message(2, self._do_init, cx_core)
def disconnect(self):
super(OSCBindingViewerComponent, self).disconnect()
self._track_name_element = None
self._device_name_element = None
def _do_init(self, cx_core):
log_bindings = False
# need to use scheduling here as user actions are created before the OSC server
# and binding components.
server = cx_core.osc_server
bc = cx_core.get_binding_component(2) #XTB Script refers to A & H mixer
if server and bc:
for i in xrange(bc.num_encoders):
enc = bc.get_encoder(i)
if log_bindings: self.canonical_parent.log_message(enc.name)
self.register_component(BindingObserver(server, '/%s' % enc.name, enc))
for i in xrange(bc.num_buttons):
btn = bc.get_button(i)
if log_bindings: self.canonical_parent.log_message(btn.name)
self.register_component(BindingObserver(server, '/%s' % btn.name, btn))
bc = cx_core.get_binding_component(4) #XTE Script refers Launchpad
if server and bc:
for i in xrange(bc.num_encoders):
enc = bc.get_encoder(i)
if log_bindings: self.canonical_parent.log_message(enc.name)
self.register_component(BindingObserver(server, '/%s' % enc.name, enc))
for i in xrange(bc.num_buttons):
btn = bc.get_button(i)
if log_bindings: self.canonical_parent.log_message(btn.name)
self.register_component(BindingObserver(server, '/%s' % btn.name, btn))
if server:
self._track_name_element = OSCElement(server, '/track/name')
self._device_name_element = OSCElement(server, '/device/name')
self.on_selected_track_changed()
def on_selected_track_changed(self):
if self._track_name_element and self._device_name_element:
self._on_track_name_changed.subject = self._song.view.selected_track
self._on_selected_device_changed.subject =\
self._song.view.selected_track.view
self._on_track_name_changed()
self._on_device_name_changed()
@subject_slot('name')
def _on_track_name_changed(self):
self._track_name_element.send_value(str(self._song.view.selected_track.name))
@subject_slot('selected_device')
def _on_selected_device_changed(self):
dev = self._song.view.selected_track.view.selected_device
self._on_device_name_changed.subject = dev if dev else None
self._on_device_name_changed()
@subject_slot('name')
def _on_device_name_changed(self):
dev = self._song.view.selected_track.view.selected_device
name = dev.name if dev else 'None'
self._device_name_element.send_value(str(name))