]>
git.immae.eu Git - perso/Immae/Projets/Python/MusicSampler.git/blob - keyboard.py
0c1115f2d49cccda6b5656e96d2c7140f3be5d3b
1 from kivy
.app
import App
2 from kivy
.uix
.widget
import Widget
3 from kivy
.uix
.floatlayout
import FloatLayout
4 from kivy
.uix
.relativelayout
import RelativeLayout
5 from kivy
.properties
import AliasProperty
, ReferenceListProperty
, BooleanProperty
, NumericProperty
, ListProperty
, StringProperty
, ObjectProperty
6 from kivy
.vector
import Vector
7 from kivy
.clock
import Clock
8 from kivy
.uix
.behaviors
import ButtonBehavior
9 from kivy
.uix
.label
import Label
10 from kivy
.core
.window
import Window
12 from helpers
.action
import *
20 class KeyDescription(Label
):
23 class Key(ButtonBehavior
, Widget
):
24 key_sym
= StringProperty(None)
25 custom_color
= ListProperty([0, 1, 0, 1])
26 custom_unready_color
= ListProperty([0, 1, 0, 100/255])
27 description_title
= StringProperty("")
28 description
= ListProperty([])
29 is_key_ready
= BooleanProperty(True)
32 if not self
.has_actions
:
34 elif self
.all_actions_ready
:
35 return self
.custom_color
37 return self
.custom_unready_color
41 color
= AliasProperty(get_color
, set_color
, bind
=['is_key_ready'])
43 def __init__(self
, **kwargs
):
44 super(Key
, self
).__init
__(**kwargs
)
47 def on_key_sym(self
, key
, key_sym
):
48 if key_sym
in self
.parent
.key_config
:
49 self
.is_key_ready
= False
51 self
.config
= self
.parent
.key_config
[key_sym
]
54 for key_action
in self
.config
['actions']:
55 self
.add_action(key_action
[0], **key_action
[1])
57 if 'description' in self
.config
['properties']:
58 key
.set_description(self
.config
['properties']['description'])
59 if 'color' in self
.config
['properties']:
60 key
.set_color(self
.config
['properties']['color'])
62 Clock
.schedule_interval(self
.check_all_active
, 1)
64 def check_all_active(self
, dt
):
65 if self
.all_actions_ready
:
66 self
.is_key_ready
= True
69 def set_description(self
, description
):
70 if description
[0] is not None:
71 self
.description_title
= str(description
[0])
72 for desc
in description
[1:]:
74 self
.description
.append("")
76 self
.description
.append(str(desc
).replace(" ", " "))
78 def set_color(self
, color
):
79 color
= [x
/ 255 for x
in color
]
81 self
.custom_color
= color
83 self
.custom_unready_color
= tuple(color
)
86 def has_actions(self
):
87 return len(self
.actions
) > 0
90 def all_actions_ready(self
):
91 return all(action
.ready() for action
in self
.actions
)
93 def add_action(self
, action_name
, **arguments
):
94 self
.actions
.append(Action(action_name
, self
, **arguments
))
97 print("running actions for {}".format(self
.key_sym
))
98 start_time
= time
.time()
99 self
.parent
.start_running(self
, start_time
)
101 for action
in self
.actions
:
102 if self
.parent
.keep_running(self
, start_time
):
103 self
.list_actions(action_number
= action_number
+ 0.5)
106 self
.list_actions(action_number
= action_number
)
108 self
.parent
.finished_running(self
, start_time
)
110 def list_actions(self
, action_number
= 0):
111 self
.parent
.parent
.ids
['ActionList'].update_list(self
, action_number
)
117 class PlayList(RelativeLayout
):
118 playlist
= ListProperty([])
120 def __init__(self
, **kwargs
):
121 super(PlayList
, self
).__init
__(**kwargs
)
122 Clock
.schedule_interval(self
.update_playlist
, 0.5)
124 def update_playlist(self
, dt
):
125 if self
.parent
is None or 'Mapping' not in self
.parent
.ids
:
128 open_files
= self
.parent
.ids
['Mapping'].open_files
130 for music_file
in open_files
.values():
131 if not music_file
.is_playing():
133 if music_file
.is_paused():
134 self
.playlist
.append(["⏸", music_file
.name
, False])
136 self
.playlist
.append(["⏵", music_file
.name
, True])
139 class ActionList(RelativeLayout
):
140 action_title
= StringProperty("")
141 action_list
= ListProperty([])
143 def update_list(self
, key
, action_number
= 0):
144 self
.action_title
= "actions linked to key {}:".format(key
.key_sym
)
145 self
.action_list
= []
147 action_descriptions
= [action
.description() for action
in key
.actions
]
149 for index
, description
in enumerate(action_descriptions
):
150 if index
< int(action_number
):
152 elif index
+ 0.5 == action_number
:
157 self
.action_list
.append([icon
, description
])
159 class Mapping(RelativeLayout
):
160 expected_keys
= NumericProperty(0)
162 def __init__(self
, **kwargs
):
163 self
.key_config
, self
.channel_number
, self
.open_files
= helpers
.parse_config2()
164 super(Mapping
, self
).__init
__(**kwargs
)
165 self
._keyboard
= Window
.request_keyboard(self
._keyboard
_closed
, self
)
166 self
._keyboard
.bind(on_key_down
=self
._on
_keyboard
_down
)
170 pygame
.mixer
.init(frequency
= 44100)
171 pygame
.mixer
.set_num_channels(self
.channel_number
)
173 def _keyboard_closed(self
):
174 self
._keyboard
.unbind(on_key_down
=self
._on
_keyboard
_down
)
175 self
._keyboard
= None
177 def _on_keyboard_down(self
, keyboard
, keycode
, text
, modifiers
):
178 key
= self
.find_by_key_code(keycode
)
180 threading
.Thread(name
= "MSKeyAction", target
=key
.do_actions
).start()
183 def find_by_key_code(self
, key_code
):
184 if "Key_" + str(key_code
[0]) in self
.ids
:
185 return self
.ids
["Key_" + str(key_code
[0])]
188 def find_by_unicode(self
, key_sym
):
189 for key
in self
.children
:
190 if not type(key
).__name
__ == "Key":
192 print(key
.key_sym
, key_sym
)
193 if key
.key_sym
== key_sym
:
198 def stop_all_running(self
):
201 def start_running(self
, key
, start_time
):
202 self
.running
.append((key
, start_time
))
204 def keep_running(self
, key
, start_time
):
205 return (key
, start_time
) in self
.running
207 def finished_running(self
, key
, start_time
):
208 if (key
, start_time
) in self
.running
:
209 self
.running
.remove((key
, start_time
))
212 class Screen(FloatLayout
):
215 class MusicSamplerApp(App
):
217 Window
.size
= (913, 563)
221 if __name__
== '__main__':
222 MusicSamplerApp().run()