X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=helpers%2Fmapping.py;h=c2a94e67baa52f3bfe4b06eccb78d88741805789;hb=205861936ca55357beea6a8af7c0c9ed5a61f484;hp=ee5623a9843f503b2f034d7fa6e8794c483ba208;hpb=be27763f8be0f647cbe17ecee8c782901ce2cede;p=perso%2FImmae%2FProjets%2FPython%2FMusicSampler.git diff --git a/helpers/mapping.py b/helpers/mapping.py index ee5623a..c2a94e6 100644 --- a/helpers/mapping.py +++ b/helpers/mapping.py @@ -1,163 +1,101 @@ +from kivy.uix.relativelayout import RelativeLayout +from kivy.properties import NumericProperty, ListProperty +from kivy.core.window import Window +from kivy.clock import Clock + import threading -import pygame -from .key import * - -class Mapping: - WIDTH = 903 - HEIGHT = 298 - SIZE = WIDTH, HEIGHT - - KEYS = [ - (pygame.K_ESCAPE, 'ESC', 'first', 0, {}), - - (pygame.K_F1, 'F1', 'first', 100, {}), - (pygame.K_F2, 'F2', 'first', 150, {}), - (pygame.K_F3, 'F3', 'first', 200, {}), - (pygame.K_F4, 'F4', 'first', 250, {}), - - (pygame.K_F5, 'F5', 'first', 325, {}), - (pygame.K_F6, 'F6', 'first', 375, {}), - (pygame.K_F7, 'F7', 'first', 425, {}), - (pygame.K_F8, 'F8', 'first', 475, {}), - - (pygame.K_F9, 'F9', 'first', 550, {}), - (pygame.K_F10, 'F10', 'first', 600, {}), - (pygame.K_F11, 'F11', 'first', 650, {}), - (pygame.K_F12, 'F12', 'first', 700, {}), - - - (178, '²', 'second', 0, {}), - (pygame.K_AMPERSAND, '&', 'second', 50, {}), - (233, 'é', 'second', 100, {}), - (pygame.K_QUOTEDBL, '"', 'second', 150, {}), - (pygame.K_QUOTE, "'", 'second', 200, {}), - (pygame.K_LEFTPAREN, '(', 'second', 250, {}), - (pygame.K_MINUS, '-', 'second', 300, {}), - (232, 'è', 'second', 350, {}), - (pygame.K_UNDERSCORE, '_', 'second', 400, {}), - (231, 'ç', 'second', 450, {}), - (224, 'à', 'second', 500, {}), - (pygame.K_RIGHTPAREN, ')', 'second', 550, {}), - (pygame.K_EQUALS, '=', 'second', 600, {}), - - (pygame.K_BACKSPACE, '<-', 'second', 650, { 'width': 98 }), - - - (pygame.K_TAB, 'tab', 'third', 0, { 'width' : 73 }), - (pygame.K_a, 'a', 'third', 75, {}), - (pygame.K_z, 'z', 'third', 125, {}), - (pygame.K_e, 'e', 'third', 175, {}), - (pygame.K_r, 'r', 'third', 225, {}), - (pygame.K_t, 't', 'third', 275, {}), - (pygame.K_y, 'y', 'third', 325, {}), - (pygame.K_u, 'u', 'third', 375, {}), - (pygame.K_i, 'i', 'third', 425, {}), - (pygame.K_o, 'o', 'third', 475, {}), - (pygame.K_p, 'p', 'third', 525, {}), - (pygame.K_CARET, '^', 'third', 575, {}), - (pygame.K_DOLLAR, '$', 'third', 625, {}), - - (pygame.K_RETURN, 'Enter', 'third', 692, { 'width': 56, 'height': 98 }), - - (pygame.K_CAPSLOCK, 'CAPS', 'fourth', 0, { 'width': 88, 'disabled': True }), - - (pygame.K_q, 'q', 'fourth', 90, {}), - (pygame.K_s, 's', 'fourth', 140, {}), - (pygame.K_d, 'd', 'fourth', 190, {}), - (pygame.K_f, 'f', 'fourth', 240, {}), - (pygame.K_g, 'g', 'fourth', 290, {}), - (pygame.K_h, 'h', 'fourth', 340, {}), - (pygame.K_j, 'j', 'fourth', 390, {}), - (pygame.K_k, 'k', 'fourth', 440, {}), - (pygame.K_l, 'l', 'fourth', 490, {}), - (pygame.K_m, 'm', 'fourth', 540, {}), - (249, 'ù', 'fourth', 590, {}), - (pygame.K_ASTERISK, '*', 'fourth', 640, {}), - - - (pygame.K_LSHIFT, 'LShift', 'fifth', 0, { 'width': 63, 'disabled': True }), - - (pygame.K_LESS, '<', 'fifth', 65, {}), - (pygame.K_w, 'w', 'fifth', 115, {}), - (pygame.K_x, 'x', 'fifth', 165, {}), - (pygame.K_c, 'c', 'fifth', 215, {}), - (pygame.K_v, 'v', 'fifth', 265, {}), - (pygame.K_b, 'b', 'fifth', 315, {}), - (pygame.K_n, 'n', 'fifth', 365, {}), - (pygame.K_COMMA, ',', 'fifth', 415, {}), - (pygame.K_SEMICOLON, ';', 'fifth', 465, {}), - (pygame.K_COLON, ':', 'fifth', 515, {}), - (pygame.K_EXCLAIM, '!', 'fifth', 565, {}), - - (pygame.K_RSHIFT, 'RShift', 'fifth', 615, { 'width': 133, 'disabled': True }), - - (pygame.K_LCTRL, 'LCtrl', 'sixth', 0, { 'width': 63, 'disabled': True }), - (pygame.K_LSUPER, 'LSuper', 'sixth', 115, { 'disabled': True }), - (pygame.K_LALT, 'LAlt', 'sixth', 165, { 'disabled': True }), - (pygame.K_SPACE, 'Espace', 'sixth', 215, { 'width': 248 }), - (pygame.K_MODE, 'AltGr', 'sixth', 465, { 'disabled': True }), - (314, 'Compose', 'sixth', 515, { 'disabled': True }), - (pygame.K_RCTRL, 'RCtrl', 'sixth', 565, { 'width': 63, 'disabled': True }), - - - (pygame.K_INSERT, 'ins', 'second', 755, {}), - (pygame.K_HOME, 'home', 'second', 805, {}), - (pygame.K_PAGEUP, 'pg_u', 'second', 855, {}), - (pygame.K_DELETE, 'del', 'third', 755, {}), - (pygame.K_END, 'end', 'third', 805, {}), - (pygame.K_PAGEDOWN, 'pg_d', 'third', 855, {}), - - - (pygame.K_UP, 'up', 'fifth', 805, {}), - (pygame.K_DOWN, 'down', 'sixth', 805, {}), - (pygame.K_LEFT, 'left', 'sixth', 755, {}), - (pygame.K_RIGHT, 'right', 'sixth', 855, {}), - ] - - def __init__(self, screen, draw_lock): - self.draw_lock = draw_lock - self.screen = screen - self.background = pygame.Surface(self.SIZE).convert() - self.background.fill((250, 250, 250)) - self.keys = {} +import yaml +import sys + +from .music_file import * +from .mixer import Mixer +from . import Config, gain, error_print +from .action import Action + +class Mapping(RelativeLayout): + expected_keys = NumericProperty(0) + master_volume = NumericProperty(100) + ready_color = ListProperty([1, 165/255, 0, 1]) + + def __init__(self, **kwargs): + if Config.builtin_mixing: + self.mixer = Mixer() + else: + self.mixer = None + + try: + self.key_config, self.open_files = self.parse_config() + except Exception as e: + error_print("Error while loading configuration: {}".format(e)) + sys.exit() + + super(Mapping, self).__init__(**kwargs) + self._keyboard = Window.request_keyboard(self._keyboard_closed, self) + self._keyboard.bind(on_key_down=self._on_keyboard_down) self.running = [] - for key in self.KEYS: - self.keys[key[0]] = Key(self, self.draw_lock, *key[0:4], **key[4]) - - def draw(self): - for key_name in self.keys: - key = self.keys[key_name] - should_redraw_key = key.draw(self.background) - - if should_redraw_key: - threading.Thread(target = key.poll_redraw, args = [self.background]).start() - self.blit() - - def blit(self): - self.draw_lock.acquire() - self.screen.blit(self.background, (5, 5)) - pygame.display.flip() - self.draw_lock.release() - - def find_by_key_num(self, key_num): - if key_num in self.keys: - return self.keys[key_num] - return None - - def find_by_collidepoint(self, position): - for key in self.keys: - if self.keys[key].collidepoint(position): - return self.keys[key] + self.wait_ids = {} + Clock.schedule_interval(self.not_all_keys_ready, 1) + + @property + def master_gain(self): + return gain(self.master_volume) + + def set_master_volume(self, value, delta=False, fade=0): + [db_gain, self.master_volume] = gain( + value + int(delta) * self.master_volume, + self.master_volume) + + for music in self.open_files.values(): + music.set_gain_with_effect(db_gain, fade=fade) + + def add_wait_id(self, wait_id, action_or_wait): + self.wait_ids[wait_id] = action_or_wait + + def interrupt_wait(self, wait_id): + if wait_id in self.wait_ids: + action_or_wait = self.wait_ids[wait_id] + del(self.wait_ids[wait_id]) + if isinstance(action_or_wait, Action): + action_or_wait.interrupt() + else: + action_or_wait.set() + + def _keyboard_closed(self): + self._keyboard.unbind(on_key_down=self._on_keyboard_down) + self._keyboard = None + + def _on_keyboard_down(self, keyboard, keycode, text, modifiers): + key = self.find_by_key_code(keycode) + if len(modifiers) == 0 and key is not None: + threading.Thread(name="MSKeyAction", target=key.do_actions).start() + elif 'ctrl' in modifiers and (keycode[0] == 113 or keycode[0] == '99'): + for thread in threading.enumerate(): + if thread.getName()[0:2] != "MS": + continue + thread.join() + + sys.exit() + return True + + def find_by_key_code(self, key_code): + if "Key_" + str(key_code[0]) in self.ids: + return self.ids["Key_" + str(key_code[0])] return None - def find_by_unicode(self, key_sym): - for key in self.keys: - if self.keys[key].key_sym == key_sym: - return self.keys[key] - return None + def not_all_keys_ready(self, dt): + for key in self.children: + if not type(key).__name__ == "Key": + continue + if not key.is_key_ready: + return True + self.ready_color = [0, 1, 0, 1] + return False def stop_all_running(self): + running = self.running self.running = [] + for (key, start_time) in running: + key.interrupt_action() def start_running(self, key, start_time): self.running.append((key, start_time)) @@ -169,3 +107,92 @@ class Mapping: if (key, start_time) in self.running: self.running.remove((key, start_time)) + def parse_config(self): + stream = open(Config.yml_file, "r") + try: + config = yaml.load(stream) + except Exception as e: + error_print("Error while loading config file: {}".format(e)) + sys.exit() + stream.close() + + aliases = config['aliases'] + seen_files = {} + + key_properties = {} + + for key in config['key_properties']: + if key not in key_properties: + key_prop = config['key_properties'][key] + if 'include' in key_prop: + included = key_prop['include'] + del(key_prop['include']) + + if isinstance(included, str): + key_prop.update(aliases[included], **key_prop) + else: + for included_ in included: + key_prop.update(aliases[included_], **key_prop) + + key_properties[key] = { + "actions": [], + "properties": key_prop, + "files": [] + } + + for mapped_key in config['keys']: + if mapped_key not in key_properties: + key_properties[mapped_key] = { + "actions": [], + "properties": {}, + "files": [] + } + for action in config['keys'][mapped_key]: + action_name = list(action)[0] + action_args = {} + if action[action_name] is None: + action[action_name] = [] + + if 'include' in action[action_name]: + included = action[action_name]['include'] + del(action[action_name]['include']) + + if isinstance(included, str): + action[action_name].update( + aliases[included], + **action[action_name]) + else: + for included_ in included: + action[action_name].update( + aliases[included_], + **action[action_name]) + + for argument in action[action_name]: + if argument == 'file': + filename = action[action_name]['file'] + if filename not in seen_files: + if filename in config['music_properties']: + seen_files[filename] = MusicFile( + filename, + self, + **config['music_properties'][filename]) + else: + seen_files[filename] = MusicFile( + self, + filename) + + if filename not in key_properties[mapped_key]['files']: + key_properties[mapped_key]['files'] \ + .append(seen_files[filename]) + + action_args['music'] = seen_files[filename] + + else: + action_args[argument] = action[action_name][argument] + + key_properties[mapped_key]['actions'] \ + .append([action_name, action_args]) + + return (key_properties, seen_files) + +