aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIsmaël Bouya <ismael.bouya@normalesup.org>2016-07-26 22:59:41 +0200
committerIsmaël Bouya <ismael.bouya@normalesup.org>2016-07-26 22:59:41 +0200
commit8ba7d831a1b8da01ba9e86491d7740f674910a53 (patch)
tree0fb7fd486e45f9feb475e606101e761b2e63c143
parentd8c3ae04cd6af07d5cdc68e09441bb4df2bbde02 (diff)
downloadMusicSampler-8ba7d831a1b8da01ba9e86491d7740f674910a53.tar.gz
MusicSampler-8ba7d831a1b8da01ba9e86491d7740f674910a53.tar.zst
MusicSampler-8ba7d831a1b8da01ba9e86491d7740f674910a53.zip
Make callbacks when key is ready
-rw-r--r--helpers/key.py17
-rw-r--r--helpers/mapping.py146
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
8import sys 8import sys
9from collections import defaultdict 9from collections import defaultdict
10 10
11from transitions.extensions import HierarchicalMachine as Machine
12
11from .music_file import MusicFile 13from .music_file import MusicFile
12from .mixer import Mixer 14from .mixer import Mixer
13from . import Config, gain, error_print, warn_print 15from . import Config, gain, error_print, warn_print
14from .action import Action 16from .action import Action
15 17
16class Mapping(RelativeLayout): 18class 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):