cuemsutils 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cuemsutils/CTimecode.py +157 -0
- cuemsutils/__init__.py +4 -0
- cuemsutils/cues/ActionCue.py +124 -0
- cuemsutils/cues/AudioCue.py +282 -0
- cuemsutils/cues/Cue.py +251 -0
- cuemsutils/cues/CueList.py +160 -0
- cuemsutils/cues/CueOutput.py +13 -0
- cuemsutils/cues/CuemsScript.py +124 -0
- cuemsutils/cues/DmxCue.py +309 -0
- cuemsutils/cues/VideoCue.py +287 -0
- cuemsutils/cues/__init__.py +0 -0
- cuemsutils/log.py +77 -0
- cuemsutils/schemas/outputs.xsd +21 -0
- cuemsutils/schemas/project_mappings.xsd +74 -0
- cuemsutils/schemas/project_settings.xsd +23 -0
- cuemsutils/schemas/script.xsd +405 -0
- cuemsutils/schemas/settings.xsd +113 -0
- cuemsutils/xml/CMLCuemsConverter.py +177 -0
- cuemsutils/xml/DictParser.py +258 -0
- cuemsutils/xml/XmlBuilder.py +299 -0
- cuemsutils/xml/XmlReaderWriter.py +84 -0
- cuemsutils/xml/__init__.py +0 -0
- cuemsutils-0.0.1.dist-info/METADATA +47 -0
- cuemsutils-0.0.1.dist-info/RECORD +26 -0
- cuemsutils-0.0.1.dist-info/WHEEL +4 -0
- cuemsutils-0.0.1.dist-info/licenses/LICENSE +674 -0
cuemsutils/CTimecode.py
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
from timecode import Timecode
|
|
2
|
+
|
|
3
|
+
#TODO: !IMPORTANT! fix milisecond parseing with more than 3 digits and leading 0's; Fix division returnig to 23:59...
|
|
4
|
+
class CTimecode(Timecode):
|
|
5
|
+
def __init__(self, init_dict = None, start_timecode=None, start_seconds=None, frames=None, framerate='ms'):
|
|
6
|
+
if init_dict is not None:
|
|
7
|
+
super().__init__(framerate, init_dict, start_seconds, frames)
|
|
8
|
+
else:
|
|
9
|
+
if start_seconds == 0:
|
|
10
|
+
start_seconds = None
|
|
11
|
+
frames = None
|
|
12
|
+
super().__init__(framerate, start_timecode, start_seconds, frames)
|
|
13
|
+
|
|
14
|
+
@classmethod
|
|
15
|
+
def from_dict(cls, init_dict):
|
|
16
|
+
return cls(init_dict = init_dict)
|
|
17
|
+
|
|
18
|
+
@property
|
|
19
|
+
def milliseconds(self):
|
|
20
|
+
"""returns time as milliseconds
|
|
21
|
+
"""
|
|
22
|
+
#TODO: float math for other framerates
|
|
23
|
+
millis_per_frame = 1000 / float(self._framerate)
|
|
24
|
+
return int(millis_per_frame * self.frame_number)
|
|
25
|
+
|
|
26
|
+
def return_in_other_framerate(self, framerate):
|
|
27
|
+
"""returns a copy of the object with a different framerate.
|
|
28
|
+
"""
|
|
29
|
+
new = CTimecode(framerate=framerate, start_seconds=float(self.milliseconds / 1000))
|
|
30
|
+
return new
|
|
31
|
+
|
|
32
|
+
def __hash__(self):
|
|
33
|
+
return hash((self.milliseconds, self.milliseconds))
|
|
34
|
+
|
|
35
|
+
def __eq__(self, other):
|
|
36
|
+
"""Compares seconds of tc""" #TODO: decide if we cheek framerate and frame equality or time equiality
|
|
37
|
+
if isinstance(other, CTimecode):
|
|
38
|
+
return self.milliseconds == other.milliseconds
|
|
39
|
+
return NotImplemented
|
|
40
|
+
|
|
41
|
+
def __ne__(self, other):
|
|
42
|
+
"""Compares seconds of tc""" #TODO: decide if we cheek framerate and frame equality or time equiality
|
|
43
|
+
if isinstance(other, CTimecode):
|
|
44
|
+
return self.milliseconds != other.milliseconds
|
|
45
|
+
return NotImplemented
|
|
46
|
+
|
|
47
|
+
def __lt__(self, other):
|
|
48
|
+
"""Compares seconds of tc""" #TODO: decide if we cheek framerate and frame equality or time equiality
|
|
49
|
+
if isinstance(other, CTimecode):
|
|
50
|
+
return self.milliseconds < other.milliseconds
|
|
51
|
+
elif isinstance(other, int):
|
|
52
|
+
return self.milliseconds < other
|
|
53
|
+
elif isinstance(other, type(None)):
|
|
54
|
+
return other
|
|
55
|
+
|
|
56
|
+
return NotImplemented
|
|
57
|
+
|
|
58
|
+
def __le__(self, other):
|
|
59
|
+
"""Compares seconds of tc""" #TODO: decide if we cheek framerate and frame equality or time equiality
|
|
60
|
+
if isinstance(other, CTimecode):
|
|
61
|
+
return self.milliseconds <= other.milliseconds
|
|
62
|
+
elif isinstance(other, type(None)):
|
|
63
|
+
return other
|
|
64
|
+
return NotImplemented
|
|
65
|
+
|
|
66
|
+
def __gt__(self, other):
|
|
67
|
+
"""Compares seconds of tc""" #TODO: decide if we cheek framerate and frame equality or time equiality
|
|
68
|
+
if isinstance(other, CTimecode):
|
|
69
|
+
return self.milliseconds > other.milliseconds
|
|
70
|
+
elif isinstance(other, int):
|
|
71
|
+
return self.milliseconds > other
|
|
72
|
+
elif isinstance(other, type(None)):
|
|
73
|
+
return self
|
|
74
|
+
return NotImplemented
|
|
75
|
+
|
|
76
|
+
def __ge__(self, other):
|
|
77
|
+
"""Compares seconds of tc""" #TODO: decide if we cheek framerate and frame equality or time equiality
|
|
78
|
+
if isinstance(other, CTimecode):
|
|
79
|
+
return self.milliseconds >= other.milliseconds
|
|
80
|
+
elif isinstance(other, type(None)):
|
|
81
|
+
return self
|
|
82
|
+
return NotImplemented
|
|
83
|
+
|
|
84
|
+
def __add__(self, other):
|
|
85
|
+
"""returns new CTimecode instance with the given timecode or frames
|
|
86
|
+
added to this one
|
|
87
|
+
"""
|
|
88
|
+
# duplicate current one
|
|
89
|
+
tc = CTimecode(framerate=self._framerate, frames=self.frames)
|
|
90
|
+
|
|
91
|
+
if isinstance(other, CTimecode):
|
|
92
|
+
tc.add_frames(other.frames)
|
|
93
|
+
elif isinstance(other, int):
|
|
94
|
+
tc.add_frames(other)
|
|
95
|
+
else:
|
|
96
|
+
raise CTimecodeError(
|
|
97
|
+
'Type %s not supported for arithmetic.' %
|
|
98
|
+
other.__class__.__name__
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
return tc
|
|
102
|
+
|
|
103
|
+
def __sub__(self, other):
|
|
104
|
+
"""returns new CTimecode instance with subtracted value"""
|
|
105
|
+
if isinstance(other, CTimecode):
|
|
106
|
+
subtracted_frames = self.frames - other.frames
|
|
107
|
+
elif isinstance(other, int):
|
|
108
|
+
subtracted_frames = self.frames - other
|
|
109
|
+
else:
|
|
110
|
+
raise CTimecodeError(
|
|
111
|
+
'Type %s not supported for arithmetic.' %
|
|
112
|
+
other.__class__.__name__
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
return CTimecode(framerate=self._framerate, frames=subtracted_frames)
|
|
116
|
+
|
|
117
|
+
def __mul__(self, other):
|
|
118
|
+
"""returns new CTimecode instance with multiplied value"""
|
|
119
|
+
if isinstance(other, CTimecode):
|
|
120
|
+
multiplied_frames = self.frames * other.frames
|
|
121
|
+
elif isinstance(other, int):
|
|
122
|
+
multiplied_frames = self.frames * other
|
|
123
|
+
else:
|
|
124
|
+
raise CTimecodeError(
|
|
125
|
+
'Type %s not supported for arithmetic.' %
|
|
126
|
+
other.__class__.__name__
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
return CTimecode(framerate=self._framerate, frames=multiplied_frames)
|
|
130
|
+
|
|
131
|
+
def __truediv__(self, other):
|
|
132
|
+
"""returns new CTimecode instance with divided value"""
|
|
133
|
+
if isinstance(other, CTimecode):
|
|
134
|
+
div_frames = self.frames / other.frames
|
|
135
|
+
elif isinstance(other, int):
|
|
136
|
+
div_frames = self.frames / other
|
|
137
|
+
else:
|
|
138
|
+
raise CTimecodeError(
|
|
139
|
+
'Type %s not supported for arithmetic.' %
|
|
140
|
+
other.__class__.__name__
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
return CTimecode(framerate=self._framerate, frames=div_frames)
|
|
144
|
+
|
|
145
|
+
def __str__(self):
|
|
146
|
+
return self.tc_to_string(*self.frames_to_tc(self.frames))
|
|
147
|
+
|
|
148
|
+
def __iter__(self):
|
|
149
|
+
yield ('timecode', self.__str__())
|
|
150
|
+
yield ('framerate', self.framerate)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
class CTimecodeError(Exception):
|
|
155
|
+
"""Raised when an error occurred in timecode calculation
|
|
156
|
+
"""
|
|
157
|
+
pass
|
cuemsutils/__init__.py
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
|
|
2
|
+
from time import sleep
|
|
3
|
+
from threading import Thread
|
|
4
|
+
|
|
5
|
+
from Cue import Cue
|
|
6
|
+
from cuemsutils.log import logged
|
|
7
|
+
|
|
8
|
+
class ActionCue(Cue):
|
|
9
|
+
def __init__(self, init_dict = None):
|
|
10
|
+
if init_dict:
|
|
11
|
+
super().__init__(init_dict)
|
|
12
|
+
|
|
13
|
+
self._action_target_object = None
|
|
14
|
+
|
|
15
|
+
@property
|
|
16
|
+
def action_type(self):
|
|
17
|
+
return super().__getitem__('action_type')
|
|
18
|
+
|
|
19
|
+
@action_type.setter
|
|
20
|
+
def action_type(self, action_type):
|
|
21
|
+
super().__setitem__('action_type', action_type)
|
|
22
|
+
|
|
23
|
+
@property
|
|
24
|
+
def action_target(self):
|
|
25
|
+
return super().__getitem__('action_target')
|
|
26
|
+
|
|
27
|
+
@action_target.setter
|
|
28
|
+
def action_target(self, action_target):
|
|
29
|
+
super().__setitem__('action_target', action_target)
|
|
30
|
+
|
|
31
|
+
def arm(self, conf, ossia, armed_list, init = False):
|
|
32
|
+
self._conf = conf
|
|
33
|
+
self._armed_list = armed_list
|
|
34
|
+
|
|
35
|
+
if not self.enabled:
|
|
36
|
+
if self.loaded and self in self._armed_list:
|
|
37
|
+
self.disarm(ossia)
|
|
38
|
+
return False
|
|
39
|
+
elif self.loaded and not init:
|
|
40
|
+
if not self in self._armed_list:
|
|
41
|
+
self._armed_list.append(self)
|
|
42
|
+
return True
|
|
43
|
+
|
|
44
|
+
self.loaded = True
|
|
45
|
+
if not self in self._armed_list:
|
|
46
|
+
self._armed_list.append(self)
|
|
47
|
+
|
|
48
|
+
return True
|
|
49
|
+
|
|
50
|
+
@logged
|
|
51
|
+
def go(self, ossia, mtc):
|
|
52
|
+
if not self.loaded:
|
|
53
|
+
raise Exception(f'{self.__class__.__name__} {self.uuid} not loaded to go')
|
|
54
|
+
else:
|
|
55
|
+
# THREADED GO
|
|
56
|
+
thread = Thread(
|
|
57
|
+
name = f'GO:{self.__class__.__name__}:{self.uuid}',
|
|
58
|
+
target = self.go_thread,
|
|
59
|
+
args = [ossia, mtc]
|
|
60
|
+
)
|
|
61
|
+
thread.start()
|
|
62
|
+
|
|
63
|
+
def go_thread(self, ossia, mtc):
|
|
64
|
+
# ARM NEXT TARGET
|
|
65
|
+
if self._target_object is not None:
|
|
66
|
+
self._target_object.arm(self._conf, ossia, self._armed_list)
|
|
67
|
+
|
|
68
|
+
# PREWAIT
|
|
69
|
+
if self.prewait > 0:
|
|
70
|
+
sleep(self.prewait.milliseconds / 1000)
|
|
71
|
+
|
|
72
|
+
# PLAY : specific audio cue stuff
|
|
73
|
+
if self.action_type == 'load':
|
|
74
|
+
self._action_target_object.arm(self._conf, ossia, self._armed_list)
|
|
75
|
+
elif self.action_type == 'unload':
|
|
76
|
+
self._action_target_object.disarm(ossia)
|
|
77
|
+
elif self.action_type == 'play':
|
|
78
|
+
self._action_target_object.go(ossia, mtc)
|
|
79
|
+
elif self.action_type == 'pause':
|
|
80
|
+
pass
|
|
81
|
+
elif self.action_type == 'stop':
|
|
82
|
+
pass
|
|
83
|
+
elif self.action_type == 'enable':
|
|
84
|
+
self._action_target_object.enabled = True
|
|
85
|
+
elif self.action_type == 'disable':
|
|
86
|
+
self._action_target_object.enabled = False
|
|
87
|
+
elif self.action_type == 'fade_in':
|
|
88
|
+
self._action_target_object.enabled = False
|
|
89
|
+
elif self.action_type == 'fade_out':
|
|
90
|
+
self._action_target_object.enabled = False
|
|
91
|
+
elif self.action_type == 'wait':
|
|
92
|
+
self._action_target_object.enabled = False
|
|
93
|
+
elif self.action_type == 'go_to':
|
|
94
|
+
self._action_target_object.enabled = False
|
|
95
|
+
elif self.action_type == 'pause_project':
|
|
96
|
+
self._action_target_object.enabled = False
|
|
97
|
+
elif self.action_type == 'resume_project':
|
|
98
|
+
self._action_target_object.enabled = False
|
|
99
|
+
|
|
100
|
+
# POSTWAIT
|
|
101
|
+
if self.postwait > 0:
|
|
102
|
+
sleep(self.postwait.milliseconds / 1000)
|
|
103
|
+
|
|
104
|
+
# POST-GO GO
|
|
105
|
+
if self.post_go == 'go':
|
|
106
|
+
self._target_object.go(ossia, mtc)
|
|
107
|
+
|
|
108
|
+
# DISARM
|
|
109
|
+
if self in self._armed_list:
|
|
110
|
+
self.disarm(ossia)
|
|
111
|
+
|
|
112
|
+
def disarm(self):
|
|
113
|
+
if self.loaded is True:
|
|
114
|
+
try:
|
|
115
|
+
if self in self._armed_list:
|
|
116
|
+
self._armed_list.remove(self)
|
|
117
|
+
except:
|
|
118
|
+
pass
|
|
119
|
+
|
|
120
|
+
self.loaded = False
|
|
121
|
+
|
|
122
|
+
return True
|
|
123
|
+
else:
|
|
124
|
+
return False
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
# from pyossia import ossia_python as ossia
|
|
2
|
+
from time import sleep
|
|
3
|
+
from threading import Thread
|
|
4
|
+
|
|
5
|
+
from Cue import Cue
|
|
6
|
+
# from ..CTimecode import CTimecode
|
|
7
|
+
# from ..players.AudioPlayer import AudioPlayer
|
|
8
|
+
# from ..OssiaServer import OssiaServer, OSCConfData, PlayerOSCConfData
|
|
9
|
+
from cuemsutils.log import logged, Logger
|
|
10
|
+
|
|
11
|
+
class AudioCue(Cue):
|
|
12
|
+
# And dinamically attach it to the ossia for remote control it
|
|
13
|
+
# OSC_AUDIOPLAYER_CONF = {'/quit' : [ossia.ValueType.Impulse, None],
|
|
14
|
+
# '/load' : [ossia.ValueType.String, None],
|
|
15
|
+
# '/vol0' : [ossia.ValueType.Float, None],
|
|
16
|
+
# '/vol1' : [ossia.ValueType.Float, None],
|
|
17
|
+
# '/volmaster' : [ossia.ValueType.Float, None],
|
|
18
|
+
# '/play' : [ossia.ValueType.Impulse, None],
|
|
19
|
+
# '/stop' : [ossia.ValueType.Impulse, None],
|
|
20
|
+
# '/stoponlost' : [ossia.ValueType.Int, None],
|
|
21
|
+
# '/mtcfollow' : [ossia.ValueType.Int, None],
|
|
22
|
+
# '/offset' : [ossia.ValueType.Float, None],
|
|
23
|
+
# '/check' : [ossia.ValueType.Impulse, None]
|
|
24
|
+
# }
|
|
25
|
+
|
|
26
|
+
def __init__(self, init_dict = None):
|
|
27
|
+
super().__init__(init_dict)
|
|
28
|
+
|
|
29
|
+
self._player = None
|
|
30
|
+
self._osc_route = None
|
|
31
|
+
|
|
32
|
+
# self.OSC_AUDIOPLAYER_CONF['/offset'] = [ossia.ValueType.Float, None]
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def master_vol(self):
|
|
36
|
+
return super().__getitem__('master_vol')
|
|
37
|
+
|
|
38
|
+
@master_vol.setter
|
|
39
|
+
def master_vol(self, master_vol):
|
|
40
|
+
super().__setitem__('master_vol', master_vol)
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def outputs(self):
|
|
44
|
+
return super().__getitem__('Outputs')
|
|
45
|
+
|
|
46
|
+
@outputs.setter
|
|
47
|
+
def outputs(self, outputs):
|
|
48
|
+
super().__setitem__('Outputs', outputs)
|
|
49
|
+
|
|
50
|
+
def player(self, player):
|
|
51
|
+
self._player = player
|
|
52
|
+
|
|
53
|
+
def osc_route(self, osc_route):
|
|
54
|
+
self._osc_route = osc_route
|
|
55
|
+
|
|
56
|
+
def arm(self, conf, ossia, armed_list, init = False):
|
|
57
|
+
self._conf = conf
|
|
58
|
+
self._armed_list = armed_list
|
|
59
|
+
|
|
60
|
+
if not self.enabled:
|
|
61
|
+
if self.loaded and self in self._armed_list:
|
|
62
|
+
self.disarm(ossia)
|
|
63
|
+
return False
|
|
64
|
+
elif self.loaded and not init:
|
|
65
|
+
if not self in self._armed_list:
|
|
66
|
+
self._armed_list.append(self)
|
|
67
|
+
return True
|
|
68
|
+
|
|
69
|
+
# Assign its own audioplayer object
|
|
70
|
+
if self._local:
|
|
71
|
+
# try:
|
|
72
|
+
# self._player = AudioPlayer(
|
|
73
|
+
# self._conf.osc_port_index,
|
|
74
|
+
# self._conf.node_conf['audioplayer']['path'],
|
|
75
|
+
# self._conf.node_conf['audioplayer']['args'],
|
|
76
|
+
# str(
|
|
77
|
+
# path.join(
|
|
78
|
+
# self._conf.library_path,
|
|
79
|
+
# 'media',
|
|
80
|
+
# self.media['file_name']
|
|
81
|
+
# )
|
|
82
|
+
# ),
|
|
83
|
+
# self.uuid
|
|
84
|
+
# )
|
|
85
|
+
# except Exception as e:
|
|
86
|
+
# raise e
|
|
87
|
+
|
|
88
|
+
self._player.start()
|
|
89
|
+
|
|
90
|
+
# And dinamically attach it to the ossia for remote control it
|
|
91
|
+
self._osc_route = f'/players/audioplayer-{self.uuid}'
|
|
92
|
+
|
|
93
|
+
# ossia.add_player_nodes(
|
|
94
|
+
# PlayerOSCConfData(
|
|
95
|
+
# device_name=self._osc_route,
|
|
96
|
+
# host=self._conf.node_conf['osc_dest_host'],
|
|
97
|
+
# in_port=self._player.port,
|
|
98
|
+
# out_port=self._player.port + 1,
|
|
99
|
+
# dictionary=self.OSC_AUDIOPLAYER_CONF
|
|
100
|
+
# )
|
|
101
|
+
# )
|
|
102
|
+
|
|
103
|
+
self.loaded = True
|
|
104
|
+
if not self in self._armed_list:
|
|
105
|
+
self._armed_list.append(self)
|
|
106
|
+
|
|
107
|
+
# POST_GO CHAINED ARM
|
|
108
|
+
if self.post_go == 'go' and self._target_object:
|
|
109
|
+
self._target_object.arm(self._conf, ossia, self._armed_list, init)
|
|
110
|
+
|
|
111
|
+
return True
|
|
112
|
+
|
|
113
|
+
@logged
|
|
114
|
+
def go(self, ossia, mtc):
|
|
115
|
+
if not self.loaded:
|
|
116
|
+
raise Exception(f'{self.__class__.__name__} {self.uuid} not loaded to go')
|
|
117
|
+
else:
|
|
118
|
+
# THREADED GO
|
|
119
|
+
self._go_thread = Thread(
|
|
120
|
+
name = f'GO:{self.__class__.__name__}:{self.uuid}',
|
|
121
|
+
target = self.go_thread_func,
|
|
122
|
+
args = [ossia, mtc]
|
|
123
|
+
)
|
|
124
|
+
self._go_thread.start()
|
|
125
|
+
|
|
126
|
+
def go_thread_func(self, ossia, mtc):
|
|
127
|
+
# ARM NEXT TARGET
|
|
128
|
+
if self.post_go != 'go' and self._target_object:
|
|
129
|
+
self._target_object.arm(self._conf, ossia, self._armed_list)
|
|
130
|
+
|
|
131
|
+
# PREWAIT
|
|
132
|
+
if self.prewait > 0:
|
|
133
|
+
sleep(self.prewait.milliseconds / 1000)
|
|
134
|
+
|
|
135
|
+
# PLAY : specific audio cue stuff
|
|
136
|
+
# Set offset
|
|
137
|
+
|
|
138
|
+
### harcoded for TODO: proto_fruta, need fixx
|
|
139
|
+
#try to make all cues start at sync at 20 second timecode!
|
|
140
|
+
harcoded_go_offset = 20000
|
|
141
|
+
|
|
142
|
+
if self._local:
|
|
143
|
+
try:
|
|
144
|
+
key = f'{self._osc_route}/offset'
|
|
145
|
+
#self._start_mtc = CTimecode(frames=mtc.main_tc.milliseconds + harcoded_go_offset)
|
|
146
|
+
|
|
147
|
+
# self._start_mtc = CTimecode(frames=harcoded_go_offset)
|
|
148
|
+
|
|
149
|
+
self._end_mtc = self._start_mtc + (self.media.regions[0].out_time - self.media.regions[0].in_time)
|
|
150
|
+
offset_to_go = float(-(self._start_mtc.milliseconds) + self.media.regions[0].in_time.milliseconds)
|
|
151
|
+
ossia.send_message(key, offset_to_go)
|
|
152
|
+
Logger.log_info(
|
|
153
|
+
f"Sending offset {offset_to_go} to {key + " " + str(ossia._oscquery_registered_nodes[key][0].value)}",
|
|
154
|
+
extra = {"caller": self.__class__.__name__}
|
|
155
|
+
)
|
|
156
|
+
except KeyError:
|
|
157
|
+
Logger.log_debug(
|
|
158
|
+
f'Key error 1 in go_callback {key}',
|
|
159
|
+
extra = {"caller": self.__class__.__name__}
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
# Connect to mtc signal
|
|
163
|
+
try:
|
|
164
|
+
key = f'{self._osc_route}/mtcfollow'
|
|
165
|
+
ossia.send_message(key, 1)
|
|
166
|
+
except KeyError:
|
|
167
|
+
Logger.log_debug(
|
|
168
|
+
f'Key error 2 in go_callback {key}',
|
|
169
|
+
extra = {"caller": self.__class__.__name__}
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
# POSTWAIT
|
|
173
|
+
if self.postwait > 0:
|
|
174
|
+
sleep(self.postwait.milliseconds / 1000)
|
|
175
|
+
|
|
176
|
+
# POST-GO GO
|
|
177
|
+
if self.post_go == 'go' and self._target_object:
|
|
178
|
+
self._target_object.go(ossia, mtc)
|
|
179
|
+
|
|
180
|
+
try:
|
|
181
|
+
loop_counter = 0
|
|
182
|
+
duration = self.media.regions[0].out_time - self.media.regions[0].in_time
|
|
183
|
+
|
|
184
|
+
while not self.media.regions[0].loop or loop_counter < self.media.regions[0].loop:
|
|
185
|
+
while self._player.is_alive() and (mtc.main_tc.milliseconds < self._end_mtc.milliseconds):
|
|
186
|
+
sleep(0.005)
|
|
187
|
+
|
|
188
|
+
if self._local:
|
|
189
|
+
# Recalculate offset and apply
|
|
190
|
+
# self._start_mtc = CTimecode(frames=mtc.main_tc.milliseconds)
|
|
191
|
+
self._end_mtc = self._start_mtc + (duration)
|
|
192
|
+
offset_to_go = float(-(self._start_mtc.milliseconds) + self.media.regions[0].in_time.milliseconds)
|
|
193
|
+
try:
|
|
194
|
+
key = f'{self._osc_route}/offset'
|
|
195
|
+
ossia.send_message(key, offset_to_go)
|
|
196
|
+
except KeyError:
|
|
197
|
+
Logger.log_debug(
|
|
198
|
+
f'Key error 3 in go_callback {key}',
|
|
199
|
+
extra = {"caller": self.__class__.__name__}
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
loop_counter += 1
|
|
203
|
+
|
|
204
|
+
if self._local:
|
|
205
|
+
try:
|
|
206
|
+
key = f'{self._osc_route}/mtcfollow'
|
|
207
|
+
ossia.send_message(key, 0)
|
|
208
|
+
except KeyError:
|
|
209
|
+
Logger.log_debug(
|
|
210
|
+
f'Key error 4 in go_callback {key}',
|
|
211
|
+
extra = {"caller": self.__class__.__name__}
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
except AttributeError:
|
|
215
|
+
pass
|
|
216
|
+
|
|
217
|
+
# POST-GO GO AT END
|
|
218
|
+
if self.post_go == 'go_at_end' and self._target_object:
|
|
219
|
+
self._target_object.go(ossia, mtc)
|
|
220
|
+
|
|
221
|
+
if self in self._armed_list:
|
|
222
|
+
self.disarm(ossia)
|
|
223
|
+
|
|
224
|
+
def disarm(self, ossia):
|
|
225
|
+
if self.loaded is True:
|
|
226
|
+
try:
|
|
227
|
+
self._conf.osc_port_index['used'].remove(self._player.port)
|
|
228
|
+
self._player.kill()
|
|
229
|
+
self._player = None
|
|
230
|
+
|
|
231
|
+
# ossia.remove_nodes( OSCConfData(device_name=self._osc_route, dictionary=self.OSC_AUDIOPLAYER_CONF) )
|
|
232
|
+
|
|
233
|
+
except Exception as e:
|
|
234
|
+
Logger.log_warning(
|
|
235
|
+
f'Could not properly unload {self.uuid} : {e}',
|
|
236
|
+
extra = {"caller": self.__class__.__name__}
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
try:
|
|
240
|
+
if self in self._armed_list:
|
|
241
|
+
self._armed_list.remove(self)
|
|
242
|
+
except:
|
|
243
|
+
pass
|
|
244
|
+
|
|
245
|
+
self.loaded = False
|
|
246
|
+
|
|
247
|
+
return True
|
|
248
|
+
else:
|
|
249
|
+
return False
|
|
250
|
+
|
|
251
|
+
def stop(self):
|
|
252
|
+
self._stop_requested = True
|
|
253
|
+
if self._player and self._player.is_alive():
|
|
254
|
+
self._player.kill()
|
|
255
|
+
|
|
256
|
+
def check_mappings(self, settings):
|
|
257
|
+
if not settings.project_node_mappings:
|
|
258
|
+
return True
|
|
259
|
+
|
|
260
|
+
found = True
|
|
261
|
+
|
|
262
|
+
map_list = ['default']
|
|
263
|
+
|
|
264
|
+
if settings.project_node_mappings['audio']['outputs']:
|
|
265
|
+
m = settings.project_node_mappings['audio']['outputs']
|
|
266
|
+
for elem in settings.project_node_mappings['audio']['outputs']:
|
|
267
|
+
for map in elem['mappings']:
|
|
268
|
+
map_list.append(map['mapped_to'])
|
|
269
|
+
|
|
270
|
+
for output in self.outputs:
|
|
271
|
+
#if output['node_uuid'] == settings.node_conf['uuid']:
|
|
272
|
+
|
|
273
|
+
if output['output_name'][:36] == settings.node_conf['uuid']:
|
|
274
|
+
self._local = True
|
|
275
|
+
if output['output_name'][37:] not in map_list:
|
|
276
|
+
found = False
|
|
277
|
+
break
|
|
278
|
+
else:
|
|
279
|
+
self._local = False
|
|
280
|
+
found = True
|
|
281
|
+
|
|
282
|
+
return found
|