diff options
author | Ismaël Bouya <ismael.bouya@normalesup.org> | 2016-07-26 16:35:05 +0200 |
---|---|---|
committer | Ismaël Bouya <ismael.bouya@normalesup.org> | 2016-07-26 16:35:05 +0200 |
commit | e4917bcc6c5355a82f05880a389d0a1fd868561d (patch) | |
tree | 56f030600ad200632d0e9030c3b470dcb846215d /helpers/action.py | |
parent | db905e0706ab9a1f92102e86f677c66371be4621 (diff) | |
parent | c4f4f2a1d330d8e09021619bbb8dcaac4df0a602 (diff) | |
download | MusicSampler-e4917bcc6c5355a82f05880a389d0a1fd868561d.tar.gz MusicSampler-e4917bcc6c5355a82f05880a389d0a1fd868561d.tar.zst MusicSampler-e4917bcc6c5355a82f05880a389d0a1fd868561d.zip |
Merge branch 'actions_cleanup'
Diffstat (limited to 'helpers/action.py')
-rw-r--r-- | helpers/action.py | 324 |
1 files changed, 86 insertions, 238 deletions
diff --git a/helpers/action.py b/helpers/action.py index ec8fcb6..1f374ec 100644 --- a/helpers/action.py +++ b/helpers/action.py | |||
@@ -1,263 +1,111 @@ | |||
1 | import threading | 1 | from transitions.extensions import HierarchicalMachine as Machine |
2 | import time | 2 | from . import debug_print, error_print |
3 | 3 | from . import actions | |
4 | from . import debug_print | ||
5 | 4 | ||
6 | class Action: | 5 | class Action: |
7 | action_types = [ | 6 | STATES = [ |
8 | 'command', | 7 | 'initial', |
9 | 'interrupt_wait', | 8 | 'loading', |
10 | 'pause', | 9 | 'failed', |
11 | 'play', | 10 | { |
12 | 'seek', | 11 | 'name': 'loaded', |
13 | 'stop', | 12 | 'children': ['running'] |
14 | 'stop_all_actions', | 13 | } |
15 | 'unpause', | 14 | ] |
16 | 'volume', | 15 | |
17 | 'wait', | 16 | TRANSITIONS = [ |
17 | { | ||
18 | 'trigger': 'load', | ||
19 | 'source': 'initial', | ||
20 | 'dest': 'loading' | ||
21 | }, | ||
22 | { | ||
23 | 'trigger': 'fail', | ||
24 | 'source': 'loading', | ||
25 | 'dest': 'failed', | ||
26 | 'after': 'poll_loaded' | ||
27 | }, | ||
28 | { | ||
29 | 'trigger': 'success', | ||
30 | 'source': 'loading', | ||
31 | 'dest': 'loaded', | ||
32 | 'after': 'poll_loaded' | ||
33 | }, | ||
34 | { | ||
35 | 'trigger': 'run', | ||
36 | 'source': 'loaded', | ||
37 | 'dest': 'loaded_running', | ||
38 | 'after': 'finish_action', | ||
39 | # if a child has no transitions, then it is bubbled to the parent, | ||
40 | # and we don't want that. Not useful in that machine precisely. | ||
41 | 'conditions': ['is_loaded'] | ||
42 | }, | ||
43 | { | ||
44 | 'trigger': 'finish_action', | ||
45 | 'source': 'loaded_running', | ||
46 | 'dest': 'loaded' | ||
47 | } | ||
18 | ] | 48 | ] |
19 | 49 | ||
20 | def __init__(self, action, key, **kwargs): | 50 | def __init__(self, action, key, **kwargs): |
21 | if action in self.action_types: | 51 | Machine(model=self, states=self.STATES, |
22 | self.action = action | 52 | transitions=self.TRANSITIONS, initial='initial', |
23 | else: | 53 | ignore_invalid_triggers=True, queued=True) |
24 | raise Exception("Unknown action {}".format(action)) | ||
25 | 54 | ||
55 | self.action = action | ||
26 | self.key = key | 56 | self.key = key |
27 | self.mapping = key.parent | 57 | self.mapping = key.parent |
28 | self.arguments = kwargs | 58 | self.arguments = kwargs |
29 | self.sleep_event = None | 59 | self.sleep_event = None |
60 | self.waiting_music = None | ||
61 | |||
62 | def is_loaded_or_failed(self): | ||
63 | return self.is_loaded(allow_substates=True) or self.is_failed() | ||
30 | 64 | ||
31 | def ready(self): | 65 | def callback_music_loaded(self, success): |
32 | if 'music' in self.arguments: | 66 | if success: |
33 | return self.arguments['music'].is_loaded(allow_substates=True) | 67 | self.success() |
34 | else: | 68 | else: |
35 | return True | 69 | self.fail() |
36 | 70 | ||
37 | def run(self): | 71 | # Machine states / events |
72 | def on_enter_loading(self): | ||
73 | if hasattr(actions, self.action): | ||
74 | if 'music' in self.arguments: | ||
75 | self.arguments['music'].subscribe_loaded(self.callback_music_loaded) | ||
76 | else: | ||
77 | self.success() | ||
78 | else: | ||
79 | error_print("Unknown action {}".format(self.action)) | ||
80 | self.fail() | ||
81 | |||
82 | def on_enter_loaded_running(self): | ||
38 | debug_print(self.description()) | 83 | debug_print(self.description()) |
39 | getattr(self, self.action)(**self.arguments) | 84 | if hasattr(actions, self.action): |
85 | getattr(actions, self.action).run(self, **self.arguments) | ||
40 | 86 | ||
41 | def description(self): | 87 | def poll_loaded(self): |
42 | return getattr(self, self.action + "_print")(**self.arguments) | 88 | self.key.callback_action_ready(self, |
89 | self.is_loaded(allow_substates=True)) | ||
43 | 90 | ||
91 | # This one cannot be in the Machine state since it would be queued to run | ||
92 | # *after* the wait is ended... | ||
44 | def interrupt(self): | 93 | def interrupt(self): |
45 | if getattr(self, self.action + "_interrupt", None): | 94 | if getattr(actions, self.action, None) and\ |
46 | return getattr(self, self.action + "_interrupt")(**self.arguments) | 95 | hasattr(getattr(actions, self.action), 'interrupt'): |
96 | return getattr(getattr(actions, self.action), 'interrupt')( | ||
97 | self, **self.arguments) | ||
47 | 98 | ||
99 | # Helpers | ||
48 | def music_list(self, music): | 100 | def music_list(self, music): |
49 | if music is not None: | 101 | if music is not None: |
50 | return [music] | 102 | return [music] |
51 | else: | 103 | else: |
52 | return self.mapping.open_files.values() | 104 | return self.mapping.open_files.values() |
53 | 105 | ||
54 | # Actions | 106 | def description(self): |
55 | def command(self, command="", **kwargs): | 107 | if hasattr(actions, self.action): |
56 | # FIXME: todo | 108 | return getattr(actions, self.action)\ |
57 | pass | 109 | .description(self, **self.arguments) |
58 | |||
59 | def pause(self, music=None, **kwargs): | ||
60 | for music in self.music_list(music): | ||
61 | if music.is_loaded_playing(): | ||
62 | music.pause() | ||
63 | |||
64 | def unpause(self, music=None, **kwargs): | ||
65 | for music in self.music_list(music): | ||
66 | if music.is_loaded_paused(): | ||
67 | music.unpause() | ||
68 | |||
69 | def play(self, music=None, fade_in=0, start_at=0, | ||
70 | restart_if_running=False, volume=100, | ||
71 | loop=0, **kwargs): | ||
72 | for music in self.music_list(music): | ||
73 | if restart_if_running: | ||
74 | if music.is_in_use(): | ||
75 | music.stop() | ||
76 | music.play( | ||
77 | volume=volume, | ||
78 | fade_in=fade_in, | ||
79 | start_at=start_at, | ||
80 | loop=loop) | ||
81 | elif not music.is_in_use(): | ||
82 | music.play( | ||
83 | volume=volume, | ||
84 | fade_in=fade_in, | ||
85 | start_at=start_at, | ||
86 | loop=loop) | ||
87 | |||
88 | def seek(self, music=None, value=0, delta=False, **kwargs): | ||
89 | for music in self.music_list(music): | ||
90 | music.seek(value=value, delta=delta) | ||
91 | |||
92 | def interrupt_wait(self, wait_id=None): | ||
93 | self.mapping.interrupt_wait(wait_id) | ||
94 | |||
95 | def stop(self, music=None, fade_out=0, wait=False, | ||
96 | set_wait_id=None, **kwargs): | ||
97 | previous = None | ||
98 | for music in self.music_list(music): | ||
99 | if music.is_loaded_paused() or music.is_loaded_playing(): | ||
100 | if previous is not None: | ||
101 | previous.stop(fade_out=fade_out) | ||
102 | previous = music | ||
103 | else: | ||
104 | music.stop(fade_out=fade_out) | ||
105 | |||
106 | if previous is not None: | ||
107 | previous.stop( | ||
108 | fade_out=fade_out, | ||
109 | wait=wait, | ||
110 | set_wait_id=set_wait_id) | ||
111 | |||
112 | def stop_all_actions(self, **kwargs): | ||
113 | self.mapping.stop_all_running() | ||
114 | |||
115 | def volume(self, music=None, value=100, fade=0, delta=False, **kwargs): | ||
116 | if music is not None: | ||
117 | music.set_volume(value, delta=delta, fade=fade) | ||
118 | else: | ||
119 | self.mapping.set_master_volume(value, delta=delta, fade=fade) | ||
120 | |||
121 | def wait(self, duration=0, music=None, set_wait_id=None, **kwargs): | ||
122 | if set_wait_id is not None: | ||
123 | self.mapping.add_wait_id(set_wait_id, self) | ||
124 | |||
125 | self.sleep_event = threading.Event() | ||
126 | |||
127 | if music is not None: | ||
128 | music.wait_end() | ||
129 | |||
130 | threading.Timer(duration, self.sleep_event.set).start() | ||
131 | self.sleep_event.wait() | ||
132 | |||
133 | # Action messages | ||
134 | def command_print(self, command="", **kwargs): | ||
135 | return "running command {}".format(command) | ||
136 | |||
137 | def interrupt_wait_print(self, wait_id=None, **kwargs): | ||
138 | return "interrupt wait with id {}".format(wait_id) | ||
139 | |||
140 | def pause_print(self, music=None, **kwargs): | ||
141 | if music is not None: | ||
142 | return "pausing « {} »".format(music.name) | ||
143 | else: | ||
144 | return "pausing all musics" | ||
145 | |||
146 | def unpause_print(self, music=None, **kwargs): | ||
147 | if music is not None: | ||
148 | return "unpausing « {} »".format(music.name) | ||
149 | else: | ||
150 | return "unpausing all musics" | ||
151 | |||
152 | def play_print(self, music=None, fade_in=0, start_at=0, | ||
153 | restart_if_running=False, volume=100, loop=0, **kwargs): | ||
154 | message = "starting " | ||
155 | if music is not None: | ||
156 | message += "« {} »".format(music.name) | ||
157 | else: | ||
158 | message += "all musics" | ||
159 | |||
160 | if start_at != 0: | ||
161 | message += " at {}s".format(start_at) | ||
162 | |||
163 | if fade_in != 0: | ||
164 | message += " with {}s fade_in".format(fade_in) | ||
165 | |||
166 | message += " at volume {}%".format(volume) | ||
167 | |||
168 | if loop > 0: | ||
169 | message += " {} times".format(loop + 1) | ||
170 | elif loop < 0: | ||
171 | message += " in loop" | ||
172 | |||
173 | if restart_if_running: | ||
174 | message += " (restarting if already running)" | ||
175 | |||
176 | return message | ||
177 | |||
178 | def stop_print(self, music=None, fade_out=0, wait=False, | ||
179 | set_wait_id=None, **kwargs): | ||
180 | |||
181 | message = "stopping " | ||
182 | if music is not None: | ||
183 | message += "music « {} »".format(music.name) | ||
184 | else: | ||
185 | message += "all musics" | ||
186 | |||
187 | if fade_out > 0: | ||
188 | message += " with {}s fadeout".format(fade_out) | ||
189 | if wait: | ||
190 | if set_wait_id is not None: | ||
191 | message += " (waiting the end of fadeout, with id {})"\ | ||
192 | .format(set_wait_id) | ||
193 | else: | ||
194 | message += " (waiting the end of fadeout)" | ||
195 | |||
196 | return message | ||
197 | |||
198 | def stop_all_actions_print(self, **kwargs): | ||
199 | return "stopping all actions" | ||
200 | |||
201 | def seek_print(self, music=None, value=0, delta=False, **kwargs): | ||
202 | if delta: | ||
203 | if music is not None: | ||
204 | return "moving music « {} » by {:+d}s" \ | ||
205 | .format(music.name, value) | ||
206 | else: | ||
207 | return "moving all musics by {:+d}s" \ | ||
208 | .format(value) | ||
209 | else: | ||
210 | if music is not None: | ||
211 | return "moving music « {} » to position {}s" \ | ||
212 | .format(music.name, value) | ||
213 | else: | ||
214 | return "moving all musics to position {}s" \ | ||
215 | .format(value) | ||
216 | |||
217 | def volume_print(self, music=None, | ||
218 | value=100, delta=False, fade=0, **kwargs): | ||
219 | message = "" | ||
220 | if delta: | ||
221 | if music is not None: | ||
222 | message += "{:+d}% to volume of « {} »" \ | ||
223 | .format(value, music.name) | ||
224 | else: | ||
225 | message += "{:+d}% to volume" \ | ||
226 | .format(value) | ||
227 | else: | ||
228 | if music is not None: | ||
229 | message += "setting volume of « {} » to {}%" \ | ||
230 | .format(music.name, value) | ||
231 | else: | ||
232 | message += "setting volume to {}%" \ | ||
233 | .format(value) | ||
234 | |||
235 | if fade > 0: | ||
236 | message += " with {}s fade".format(fade) | ||
237 | |||
238 | return message | ||
239 | |||
240 | def wait_print(self, duration=0, music=None, set_wait_id=None, **kwargs): | ||
241 | message = "" | ||
242 | if music is None: | ||
243 | message += "waiting {}s" \ | ||
244 | .format(duration) | ||
245 | elif duration == 0: | ||
246 | message += "waiting the end of « {} »" \ | ||
247 | .format(music.name) | ||
248 | else: | 110 | else: |
249 | message += "waiting the end of « {} » + {}s" \ | 111 | return "unknown action {}".format(self.action) |
250 | .format(music.name, duration) | ||
251 | |||
252 | if set_wait_id is not None: | ||
253 | message += " (setting id = {})".format(set_wait_id) | ||
254 | |||
255 | return message | ||
256 | |||
257 | # Interruptions | ||
258 | def wait_interrupt(self, duration=0, music=None, **kwargs): | ||
259 | if self.sleep_event is not None: | ||
260 | self.sleep_event.set() | ||
261 | if music is not None: | ||
262 | music.wait_event.set() | ||
263 | |||