diff options
author | Ismaël Bouya <ismael.bouya@normalesup.org> | 2016-07-26 22:59:41 +0200 |
---|---|---|
committer | Ismaël Bouya <ismael.bouya@normalesup.org> | 2016-07-26 22:59:41 +0200 |
commit | 8ba7d831a1b8da01ba9e86491d7740f674910a53 (patch) | |
tree | 0fb7fd486e45f9feb475e606101e761b2e63c143 | |
parent | d8c3ae04cd6af07d5cdc68e09441bb4df2bbde02 (diff) | |
download | MusicSampler-8ba7d831a1b8da01ba9e86491d7740f674910a53.tar.gz MusicSampler-8ba7d831a1b8da01ba9e86491d7740f674910a53.tar.zst MusicSampler-8ba7d831a1b8da01ba9e86491d7740f674910a53.zip |
Make callbacks when key is ready
-rw-r--r-- | helpers/key.py | 17 | ||||
-rw-r--r-- | helpers/mapping.py | 146 |
2 files changed, 122 insertions, 41 deletions
diff --git a/helpers/key.py b/helpers/key.py index bf46eeb..7ad0565 100644 --- a/helpers/key.py +++ b/helpers/key.py | |||
@@ -30,7 +30,8 @@ class Key(ButtonBehavior, Widget): | |||
30 | { | 30 | { |
31 | 'trigger': 'fail', | 31 | 'trigger': 'fail', |
32 | 'source': 'configuring', | 32 | 'source': 'configuring', |
33 | 'dest': 'failed' | 33 | 'dest': 'failed', |
34 | 'after': 'key_loaded_callback' | ||
34 | }, | 35 | }, |
35 | { | 36 | { |
36 | 'trigger': 'success', | 37 | 'trigger': 'success', |
@@ -42,6 +43,7 @@ class Key(ButtonBehavior, Widget): | |||
42 | 'trigger': 'no_config', | 43 | 'trigger': 'no_config', |
43 | 'source': 'configuring', | 44 | 'source': 'configuring', |
44 | 'dest': 'loaded_no_config', | 45 | 'dest': 'loaded_no_config', |
46 | 'after': 'key_loaded_callback' | ||
45 | }, | 47 | }, |
46 | { | 48 | { |
47 | 'trigger': 'load', | 49 | 'trigger': 'load', |
@@ -51,22 +53,26 @@ class Key(ButtonBehavior, Widget): | |||
51 | { | 53 | { |
52 | 'trigger': 'fail', | 54 | 'trigger': 'fail', |
53 | 'source': 'loading', | 55 | 'source': 'loading', |
54 | 'dest': 'failed' | 56 | 'dest': 'failed', |
57 | 'after': 'key_loaded_callback' | ||
55 | }, | 58 | }, |
56 | { | 59 | { |
57 | 'trigger': 'success', | 60 | 'trigger': 'success', |
58 | 'source': 'loading', | 61 | 'source': 'loading', |
59 | 'dest': 'loaded' | 62 | 'dest': 'loaded', |
63 | 'after': 'key_loaded_callback' | ||
60 | }, | 64 | }, |
61 | { | 65 | { |
62 | 'trigger': 'no_actions', | 66 | 'trigger': 'no_actions', |
63 | 'source': 'loading', | 67 | 'source': 'loading', |
64 | 'dest': 'loaded_no_actions', | 68 | 'dest': 'loaded_no_actions', |
69 | 'after': 'key_loaded_callback' | ||
65 | }, | 70 | }, |
66 | { | 71 | { |
67 | 'trigger': 'reload', | 72 | 'trigger': 'reload', |
68 | 'source': 'loaded', | 73 | 'source': 'loaded', |
69 | 'dest': 'configuring' | 74 | 'dest': 'configuring', |
75 | 'after': 'key_loaded_callback' | ||
70 | }, | 76 | }, |
71 | { | 77 | { |
72 | 'trigger': 'run', | 78 | 'trigger': 'run', |
@@ -171,6 +177,9 @@ class Key(ButtonBehavior, Widget): | |||
171 | self.current_action.interrupt() | 177 | self.current_action.interrupt() |
172 | 178 | ||
173 | # Callbacks | 179 | # Callbacks |
180 | def key_loaded_callback(self): | ||
181 | self.parent.key_loaded_callback() | ||
182 | |||
174 | def callback_action_ready(self, action, success): | 183 | def callback_action_ready(self, action, success): |
175 | if not success: | 184 | if not success: |
176 | self.fail() | 185 | self.fail() |
diff --git a/helpers/mapping.py b/helpers/mapping.py index 6e3b291..9c05972 100644 --- a/helpers/mapping.py +++ b/helpers/mapping.py | |||
@@ -8,13 +8,57 @@ import yaml | |||
8 | import sys | 8 | import sys |
9 | from collections import defaultdict | 9 | from collections import defaultdict |
10 | 10 | ||
11 | from transitions.extensions import HierarchicalMachine as Machine | ||
12 | |||
11 | from .music_file import MusicFile | 13 | from .music_file import MusicFile |
12 | from .mixer import Mixer | 14 | from .mixer import Mixer |
13 | from . import Config, gain, error_print, warn_print | 15 | from . import Config, gain, error_print, warn_print |
14 | from .action import Action | 16 | from .action import Action |
15 | 17 | ||
16 | class Mapping(RelativeLayout): | 18 | class Mapping(RelativeLayout): |
17 | expected_keys = NumericProperty(0) | 19 | STATES = [ |
20 | 'initial', | ||
21 | 'configuring', | ||
22 | 'configured', | ||
23 | 'loading', | ||
24 | 'loaded', | ||
25 | 'failed' | ||
26 | ] | ||
27 | |||
28 | TRANSITIONS = [ | ||
29 | { | ||
30 | 'trigger': 'configure', | ||
31 | 'source': 'initial', | ||
32 | 'dest': 'configuring' | ||
33 | }, | ||
34 | { | ||
35 | 'trigger': 'fail', | ||
36 | 'source': 'configuring', | ||
37 | 'dest': 'failed' | ||
38 | }, | ||
39 | { | ||
40 | 'trigger': 'success', | ||
41 | 'source': 'configuring', | ||
42 | 'dest': 'configured', | ||
43 | 'after': 'load' | ||
44 | }, | ||
45 | { | ||
46 | 'trigger': 'load', | ||
47 | 'source': 'configured', | ||
48 | 'dest': 'loading' | ||
49 | }, | ||
50 | { | ||
51 | 'trigger': 'fail', | ||
52 | 'source': 'loading', | ||
53 | 'dest': 'failed' | ||
54 | }, | ||
55 | { | ||
56 | 'trigger': 'success', | ||
57 | 'source': 'loading', | ||
58 | 'dest': 'loaded' | ||
59 | } | ||
60 | ] | ||
61 | |||
18 | master_volume = NumericProperty(100) | 62 | master_volume = NumericProperty(100) |
19 | ready_color = ListProperty([1, 165/255, 0, 1]) | 63 | ready_color = ListProperty([1, 165/255, 0, 1]) |
20 | 64 | ||
@@ -31,42 +75,30 @@ class Mapping(RelativeLayout): | |||
31 | with_trace=True) | 75 | with_trace=True) |
32 | sys.exit() | 76 | sys.exit() |
33 | 77 | ||
34 | super(Mapping, self).__init__(**kwargs) | 78 | self.keys = [] |
35 | self._keyboard = Window.request_keyboard(self._keyboard_closed, self) | ||
36 | self._keyboard.bind(on_key_down=self._on_keyboard_down) | ||
37 | self.running = [] | 79 | self.running = [] |
38 | self.wait_ids = {} | 80 | self.wait_ids = {} |
39 | Clock.schedule_interval(self.not_all_keys_ready, 1) | ||
40 | |||
41 | @property | ||
42 | def master_gain(self): | ||
43 | return gain(self.master_volume) | ||
44 | 81 | ||
45 | def set_master_volume(self, value, delta=False, fade=0): | 82 | super(Mapping, self).__init__(**kwargs) |
46 | [db_gain, self.master_volume] = gain( | 83 | self.keyboard = Window.request_keyboard(self.on_keyboard_closed, self) |
47 | value + int(delta) * self.master_volume, | 84 | self.keyboard.bind(on_key_down=self.on_keyboard_down) |
48 | self.master_volume) | ||
49 | |||
50 | for music in self.open_files.values(): | ||
51 | music.set_gain_with_effect(db_gain, fade=fade) | ||
52 | 85 | ||
53 | def add_wait_id(self, wait_id, action_or_wait): | 86 | # Kivy events |
54 | self.wait_ids[wait_id] = action_or_wait | 87 | def add_widget(self, widget, index=0): |
88 | if type(widget).__name__ == "Key" and widget not in self.keys: | ||
89 | self.keys.append(widget) | ||
90 | return super(Mapping, self).add_widget(widget, index) | ||
55 | 91 | ||
56 | def interrupt_wait(self, wait_id): | 92 | def remove_widget(self, widget, index=0): |
57 | if wait_id in self.wait_ids: | 93 | if type(widget).__name__ == "Key" and widget in self.keys: |
58 | action_or_wait = self.wait_ids[wait_id] | 94 | self.keys.remove(widget) |
59 | del(self.wait_ids[wait_id]) | 95 | return super(Mapping, self).remove_widget(widget, index) |
60 | if isinstance(action_or_wait, Action): | ||
61 | action_or_wait.interrupt() | ||
62 | else: | ||
63 | action_or_wait.set() | ||
64 | 96 | ||
65 | def _keyboard_closed(self): | 97 | def on_keyboard_closed(self): |
66 | self._keyboard.unbind(on_key_down=self._on_keyboard_down) | 98 | self.keyboard.unbind(on_key_down=self.on_keyboard_down) |
67 | self._keyboard = None | 99 | self.keyboard = None |
68 | 100 | ||
69 | def _on_keyboard_down(self, keyboard, keycode, text, modifiers): | 101 | def on_keyboard_down(self, keyboard, keycode, text, modifiers): |
70 | key = self.find_by_key_code(keycode) | 102 | key = self.find_by_key_code(keycode) |
71 | if len(modifiers) == 0 and key is not None: | 103 | if len(modifiers) == 0 and key is not None: |
72 | threading.Thread(name="MSKeyAction", target=key.run).start() | 104 | threading.Thread(name="MSKeyAction", target=key.run).start() |
@@ -85,21 +117,60 @@ class Mapping(RelativeLayout): | |||
85 | return self.ids["Key_" + str(key_code[0])] | 117 | return self.ids["Key_" + str(key_code[0])] |
86 | return None | 118 | return None |
87 | 119 | ||
88 | def not_all_keys_ready(self, dt): | 120 | def all_keys_ready(self): |
89 | for key in self.children: | 121 | partial = False |
90 | if not type(key).__name__ == "Key": | 122 | for key in self.keys: |
91 | continue | ||
92 | if not key.is_loaded_or_failed(): | 123 | if not key.is_loaded_or_failed(): |
93 | return True | 124 | return "not_ready" |
94 | self.ready_color = [0, 1, 0, 1] | 125 | partial = partial or key.is_failed() |
95 | return False | ||
96 | 126 | ||
127 | if partial: | ||
128 | return "partial" | ||
129 | else: | ||
130 | return "success" | ||
131 | |||
132 | # Callbacks | ||
133 | def key_loaded_callback(self): | ||
134 | result = self.all_keys_ready() | ||
135 | if result == "success": | ||
136 | self.ready_color = [0, 1, 0, 1] | ||
137 | elif result == "partial": | ||
138 | self.ready_color = [1, 0, 0, 1] | ||
139 | |||
140 | ## Some global actions | ||
97 | def stop_all_running(self): | 141 | def stop_all_running(self): |
98 | running = self.running | 142 | running = self.running |
99 | self.running = [] | 143 | self.running = [] |
100 | for (key, start_time) in running: | 144 | for (key, start_time) in running: |
101 | key.interrupt() | 145 | key.interrupt() |
102 | 146 | ||
147 | # Master volume methods | ||
148 | @property | ||
149 | def master_gain(self): | ||
150 | return gain(self.master_volume) | ||
151 | |||
152 | def set_master_volume(self, value, delta=False, fade=0): | ||
153 | [db_gain, self.master_volume] = gain( | ||
154 | value + int(delta) * self.master_volume, | ||
155 | self.master_volume) | ||
156 | |||
157 | for music in self.open_files.values(): | ||
158 | music.set_gain_with_effect(db_gain, fade=fade) | ||
159 | |||
160 | # Wait handler methods | ||
161 | def add_wait_id(self, wait_id, action_or_wait): | ||
162 | self.wait_ids[wait_id] = action_or_wait | ||
163 | |||
164 | def interrupt_wait(self, wait_id): | ||
165 | if wait_id in self.wait_ids: | ||
166 | action_or_wait = self.wait_ids[wait_id] | ||
167 | del(self.wait_ids[wait_id]) | ||
168 | if isinstance(action_or_wait, Action): | ||
169 | action_or_wait.interrupt() | ||
170 | else: | ||
171 | action_or_wait.set() | ||
172 | |||
173 | # Methods to control running keys | ||
103 | def start_running(self, key, start_time): | 174 | def start_running(self, key, start_time): |
104 | self.running.append((key, start_time)) | 175 | self.running.append((key, start_time)) |
105 | 176 | ||
@@ -110,6 +181,7 @@ class Mapping(RelativeLayout): | |||
110 | if (key, start_time) in self.running: | 181 | if (key, start_time) in self.running: |
111 | self.running.remove((key, start_time)) | 182 | self.running.remove((key, start_time)) |
112 | 183 | ||
184 | # YML config parser | ||
113 | def parse_config(self): | 185 | def parse_config(self): |
114 | def update_alias(prop_hash, aliases, key): | 186 | def update_alias(prop_hash, aliases, key): |
115 | if isinstance(aliases[key], dict): | 187 | if isinstance(aliases[key], dict): |