]>
Commit | Line | Data |
---|---|---|
0deb82a5 | 1 | import threading |
be27763f IB |
2 | import time |
3 | ||
b7ca3fc2 IB |
4 | from transitions.extensions import HierarchicalMachine as Machine |
5 | from . import debug_print, error_print | |
a24c34bc | 6 | |
be27763f | 7 | class Action: |
b7ca3fc2 | 8 | ACTION_TYPES = [ |
be27763f | 9 | 'command', |
3aaddc9d | 10 | 'interrupt_wait', |
be27763f IB |
11 | 'pause', |
12 | 'play', | |
52d58baf | 13 | 'seek', |
be27763f IB |
14 | 'stop', |
15 | 'stop_all_actions', | |
9de92b6d | 16 | 'unpause', |
be27763f IB |
17 | 'volume', |
18 | 'wait', | |
19 | ] | |
20 | ||
b7ca3fc2 IB |
21 | STATES = [ |
22 | 'initial', | |
23 | 'loading', | |
24 | 'failed', | |
25 | { | |
26 | 'name': 'loaded', | |
27 | 'children': ['running'] | |
28 | } | |
29 | ] | |
30 | ||
31 | TRANSITIONS = [ | |
32 | { | |
33 | 'trigger': 'load', | |
34 | 'source': 'initial', | |
35 | 'dest': 'loading' | |
36 | }, | |
37 | { | |
38 | 'trigger': 'fail', | |
39 | 'source': 'loading', | |
40 | 'dest': 'failed' | |
41 | }, | |
42 | { | |
43 | 'trigger': 'success', | |
44 | 'source': 'loading', | |
45 | 'dest': 'loaded' | |
46 | }, | |
47 | { | |
48 | 'trigger': 'run', | |
49 | 'source': 'loaded', | |
50 | 'dest': 'loaded_running', | |
51 | 'after': 'finish_action' | |
52 | }, | |
53 | { | |
54 | 'trigger': 'interrupt', | |
55 | 'source': 'loaded_running', | |
56 | 'dest': 'loaded', | |
57 | 'before': 'trigger_interrupt' | |
58 | }, | |
59 | { | |
60 | 'trigger': 'finish_action', | |
61 | 'source': 'loaded_running', | |
62 | 'dest': 'loaded' | |
63 | } | |
64 | ] | |
65 | ||
be27763f | 66 | def __init__(self, action, key, **kwargs): |
b7ca3fc2 IB |
67 | Machine(model=self, states=self.STATES, |
68 | transitions=self.TRANSITIONS, initial='initial', | |
69 | ignore_invalid_triggers=True, queued=True) | |
be27763f | 70 | |
b7ca3fc2 | 71 | self.action = action |
be27763f | 72 | self.key = key |
1b4b78f5 | 73 | self.mapping = key.parent |
be27763f | 74 | self.arguments = kwargs |
0deb82a5 | 75 | self.sleep_event = None |
b7ca3fc2 IB |
76 | self.waiting_music = None |
77 | self.load() | |
be27763f IB |
78 | |
79 | def ready(self): | |
b7ca3fc2 IB |
80 | return self.is_loaded(allow_substates=True) |
81 | ||
82 | def callback_loaded(self, success): | |
83 | if success: | |
84 | self.success() | |
85 | else: | |
86 | self.fail() | |
87 | ||
88 | # Machine states / events | |
89 | def on_enter_loading(self): | |
90 | if self.action in self.ACTION_TYPES: | |
91 | if 'music' in self.arguments: | |
92 | self.arguments['music'].subscribe_loaded(self.callback_loaded) | |
93 | else: | |
94 | self.success() | |
be27763f | 95 | else: |
b7ca3fc2 IB |
96 | error_print("Unknown action {}".format(self.action)) |
97 | self.fail() | |
98 | ||
be27763f | 99 | |
b7ca3fc2 | 100 | def on_enter_loaded_running(self): |
a24c34bc | 101 | debug_print(self.description()) |
9de92b6d | 102 | getattr(self, self.action)(**self.arguments) |
be27763f | 103 | |
b7ca3fc2 | 104 | def trigger_interrupt(self): |
0deb82a5 IB |
105 | if getattr(self, self.action + "_interrupt", None): |
106 | return getattr(self, self.action + "_interrupt")(**self.arguments) | |
107 | ||
b7ca3fc2 | 108 | # Helpers |
29597680 | 109 | def music_list(self, music): |
be27763f | 110 | if music is not None: |
29597680 | 111 | return [music] |
be27763f | 112 | else: |
1b4b78f5 | 113 | return self.mapping.open_files.values() |
29597680 | 114 | |
b7ca3fc2 IB |
115 | def description(self): |
116 | if getattr(self, self.action + "_print", None): | |
117 | return getattr(self, self.action + "_print")(**self.arguments) | |
118 | else: | |
119 | return "unknown action {}".format(self.action) | |
120 | ||
c80ff6dc | 121 | # Actions |
2e404903 | 122 | def command(self, command="", **kwargs): |
c80ff6dc IB |
123 | # FIXME: todo |
124 | pass | |
125 | ||
2e404903 | 126 | def pause(self, music=None, **kwargs): |
29597680 IB |
127 | for music in self.music_list(music): |
128 | if music.is_loaded_playing(): | |
129 | music.pause() | |
be27763f | 130 | |
2e404903 | 131 | def unpause(self, music=None, **kwargs): |
29597680 IB |
132 | for music in self.music_list(music): |
133 | if music.is_loaded_paused(): | |
134 | music.unpause() | |
9de92b6d | 135 | |
2e404903 IB |
136 | def play(self, music=None, fade_in=0, start_at=0, |
137 | restart_if_running=False, volume=100, | |
138 | loop=0, **kwargs): | |
c80ff6dc | 139 | for music in self.music_list(music): |
0e5d59f7 | 140 | if restart_if_running: |
20586193 | 141 | if music.is_in_use(): |
0e5d59f7 | 142 | music.stop() |
2e404903 IB |
143 | music.play( |
144 | volume=volume, | |
145 | fade_in=fade_in, | |
146 | start_at=start_at, | |
147 | loop=loop) | |
20586193 | 148 | elif not music.is_in_use(): |
2e404903 IB |
149 | music.play( |
150 | volume=volume, | |
151 | fade_in=fade_in, | |
152 | start_at=start_at, | |
153 | loop=loop) | |
154 | ||
155 | def seek(self, music=None, value=0, delta=False, **kwargs): | |
52d58baf | 156 | for music in self.music_list(music): |
2e404903 | 157 | music.seek(value=value, delta=delta) |
52d58baf | 158 | |
3aaddc9d IB |
159 | def interrupt_wait(self, wait_id=None): |
160 | self.mapping.interrupt_wait(wait_id) | |
161 | ||
162 | def stop(self, music=None, fade_out=0, wait=False, | |
163 | set_wait_id=None, **kwargs): | |
1b4b78f5 | 164 | previous = None |
29597680 IB |
165 | for music in self.music_list(music): |
166 | if music.is_loaded_paused() or music.is_loaded_playing(): | |
1b4b78f5 | 167 | if previous is not None: |
2e404903 | 168 | previous.stop(fade_out=fade_out) |
1b4b78f5 | 169 | previous = music |
51322669 IB |
170 | else: |
171 | music.stop(fade_out=fade_out) | |
1b4b78f5 IB |
172 | |
173 | if previous is not None: | |
b7ca3fc2 | 174 | self.waiting_music = previous |
3aaddc9d IB |
175 | previous.stop( |
176 | fade_out=fade_out, | |
177 | wait=wait, | |
178 | set_wait_id=set_wait_id) | |
be27763f IB |
179 | |
180 | def stop_all_actions(self, **kwargs): | |
1b4b78f5 | 181 | self.mapping.stop_all_running() |
be27763f | 182 | |
aee1334c | 183 | def volume(self, music=None, value=100, fade=0, delta=False, **kwargs): |
0e5d59f7 | 184 | if music is not None: |
aee1334c | 185 | music.set_volume(value, delta=delta, fade=fade) |
0e5d59f7 | 186 | else: |
a8340c5d | 187 | self.mapping.set_master_volume(value, delta=delta, fade=fade) |
be27763f | 188 | |
3aaddc9d IB |
189 | def wait(self, duration=0, music=None, set_wait_id=None, **kwargs): |
190 | if set_wait_id is not None: | |
191 | self.mapping.add_wait_id(set_wait_id, self) | |
192 | ||
0deb82a5 IB |
193 | self.sleep_event = threading.Event() |
194 | ||
195 | if music is not None: | |
b86db9f1 | 196 | music.wait_end() |
be27763f | 197 | |
0deb82a5 IB |
198 | threading.Timer(duration, self.sleep_event.set).start() |
199 | self.sleep_event.wait() | |
200 | ||
201 | # Action messages | |
2e404903 | 202 | def command_print(self, command="", **kwargs): |
be27763f IB |
203 | return "running command {}".format(command) |
204 | ||
3aaddc9d IB |
205 | def interrupt_wait_print(self, wait_id=None, **kwargs): |
206 | return "interrupt wait with id {}".format(wait_id) | |
207 | ||
2e404903 | 208 | def pause_print(self, music=None, **kwargs): |
be27763f | 209 | if music is not None: |
9de92b6d | 210 | return "pausing « {} »".format(music.name) |
be27763f IB |
211 | else: |
212 | return "pausing all musics" | |
213 | ||
2e404903 | 214 | def unpause_print(self, music=None, **kwargs): |
9de92b6d IB |
215 | if music is not None: |
216 | return "unpausing « {} »".format(music.name) | |
217 | else: | |
218 | return "unpausing all musics" | |
219 | ||
2e404903 IB |
220 | def play_print(self, music=None, fade_in=0, start_at=0, |
221 | restart_if_running=False, volume=100, loop=0, **kwargs): | |
be27763f IB |
222 | message = "starting " |
223 | if music is not None: | |
9de92b6d | 224 | message += "« {} »".format(music.name) |
be27763f | 225 | else: |
c80ff6dc | 226 | message += "all musics" |
be27763f IB |
227 | |
228 | if start_at != 0: | |
229 | message += " at {}s".format(start_at) | |
230 | ||
231 | if fade_in != 0: | |
232 | message += " with {}s fade_in".format(fade_in) | |
233 | ||
234 | message += " at volume {}%".format(volume) | |
235 | ||
6f4944c1 IB |
236 | if loop > 0: |
237 | message += " {} times".format(loop + 1) | |
238 | elif loop < 0: | |
239 | message += " in loop" | |
240 | ||
be27763f IB |
241 | if restart_if_running: |
242 | message += " (restarting if already running)" | |
243 | ||
244 | return message | |
245 | ||
3aaddc9d IB |
246 | def stop_print(self, music=None, fade_out=0, wait=False, |
247 | set_wait_id=None, **kwargs): | |
248 | ||
1b4b78f5 | 249 | message = "stopping " |
be27763f | 250 | if music is not None: |
1b4b78f5 | 251 | message += "music « {} »".format(music.name) |
be27763f | 252 | else: |
1b4b78f5 IB |
253 | message += "all musics" |
254 | ||
255 | if fade_out > 0: | |
256 | message += " with {}s fadeout".format(fade_out) | |
257 | if wait: | |
3aaddc9d IB |
258 | if set_wait_id is not None: |
259 | message += " (waiting the end of fadeout, with id {})"\ | |
260 | .format(set_wait_id) | |
261 | else: | |
262 | message += " (waiting the end of fadeout)" | |
1b4b78f5 IB |
263 | |
264 | return message | |
be27763f | 265 | |
0e5d59f7 | 266 | def stop_all_actions_print(self, **kwargs): |
be27763f IB |
267 | return "stopping all actions" |
268 | ||
2e404903 | 269 | def seek_print(self, music=None, value=0, delta=False, **kwargs): |
52d58baf IB |
270 | if delta: |
271 | if music is not None: | |
2e404903 IB |
272 | return "moving music « {} » by {:+d}s" \ |
273 | .format(music.name, value) | |
52d58baf | 274 | else: |
2e404903 IB |
275 | return "moving all musics by {:+d}s" \ |
276 | .format(value) | |
52d58baf IB |
277 | else: |
278 | if music is not None: | |
2e404903 IB |
279 | return "moving music « {} » to position {}s" \ |
280 | .format(music.name, value) | |
52d58baf | 281 | else: |
2e404903 IB |
282 | return "moving all musics to position {}s" \ |
283 | .format(value) | |
52d58baf | 284 | |
aee1334c IB |
285 | def volume_print(self, music=None, |
286 | value=100, delta=False, fade=0, **kwargs): | |
287 | message = "" | |
8e50011c | 288 | if delta: |
1b4b78f5 | 289 | if music is not None: |
aee1334c | 290 | message += "{:+d}% to volume of « {} »" \ |
2e404903 | 291 | .format(value, music.name) |
1b4b78f5 | 292 | else: |
aee1334c | 293 | message += "{:+d}% to volume" \ |
2e404903 | 294 | .format(value) |
be27763f | 295 | else: |
1b4b78f5 | 296 | if music is not None: |
aee1334c | 297 | message += "setting volume of « {} » to {}%" \ |
2e404903 | 298 | .format(music.name, value) |
1b4b78f5 | 299 | else: |
aee1334c | 300 | message += "setting volume to {}%" \ |
2e404903 | 301 | .format(value) |
be27763f | 302 | |
a8340c5d | 303 | if fade > 0: |
aee1334c IB |
304 | message += " with {}s fade".format(fade) |
305 | ||
306 | return message | |
307 | ||
3aaddc9d IB |
308 | def wait_print(self, duration=0, music=None, set_wait_id=None, **kwargs): |
309 | message = "" | |
0deb82a5 | 310 | if music is None: |
3aaddc9d | 311 | message += "waiting {}s" \ |
2e404903 | 312 | .format(duration) |
0deb82a5 | 313 | elif duration == 0: |
3aaddc9d | 314 | message += "waiting the end of « {} »" \ |
2e404903 | 315 | .format(music.name) |
0deb82a5 | 316 | else: |
3aaddc9d | 317 | message += "waiting the end of « {} » + {}s" \ |
2e404903 | 318 | .format(music.name, duration) |
be27763f | 319 | |
3aaddc9d IB |
320 | if set_wait_id is not None: |
321 | message += " (setting id = {})".format(set_wait_id) | |
322 | ||
323 | return message | |
be27763f | 324 | |
b7ca3fc2 | 325 | # Interruptions (only for non-"atomic" actions) |
2e404903 | 326 | def wait_interrupt(self, duration=0, music=None, **kwargs): |
0deb82a5 IB |
327 | if self.sleep_event is not None: |
328 | self.sleep_event.set() | |
329 | if music is not None: | |
330 | music.wait_event.set() | |
331 | ||
b7ca3fc2 IB |
332 | def stop_interrupt(self, music=None, fade_out=0, wait=False, |
333 | set_wait_id=None, **kwargs): | |
334 | if self.waiting_music is not None: | |
335 | self.waiting_music.wait_event.set() |