X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=helpers%2F__init__.py;h=534e168a6898790d66cf1bdba1b88674ba12d3b3;hb=05d0d2ed0672aeb2e056c8af79bebde9c8b27199;hp=7dbffdd1641c34f960d53512a624efec38251ffc;hpb=1b79473633242db1c180e07d7165a5bd837eb62e;p=perso%2FImmae%2FProjets%2FPython%2FMusicSampler.git diff --git a/helpers/__init__.py b/helpers/__init__.py index 7dbffdd..534e168 100644 --- a/helpers/__init__.py +++ b/helpers/__init__.py @@ -1,365 +1,150 @@ # -*- coding: utf-8 -*- -from pygame import * +import argparse import sys - -class Action: - action_types = [ - 'command', - 'pause', - 'play', - 'stop', - 'volume', - 'wait', - ] - - def __init__(self, action, **kwargs): - if action in self.action_types: - self.action = action - else: - raise Exception("Unknown action {}".format(action)) - - self.arguments = kwargs - - def run(self, callback): - getattr(self, self.action)(callback, **self.arguments) - - def command(self, callback, command = "", **kwargs): - pass - - def pause(self, callback, music = None, **kwargs): - pass - - def play(self, callback, - music = None, - fade_in = 0, - restart_if_running = False, - volume = 100, - **kwargs): - pass - - def stop(self, callback, music = None, fade_out = 0, **kwargs): - print('stopping') - return callback() - - def volume(self, callback, music = None, value = 100, **kwargs): - pass - - def wait(self, callback, time = 0, **kwargs): - pass - -class Key: - row_positions = { - 'first': 5, - 'second': 55, - 'third': 105, - 'fourth': 155, - 'fifth': 205, - 'sixth': 255, - } - - default_outer_color = (120, 120, 120) - lighter_outer_color = (200, 200, 200) - default_inner_color = (255, 255, 255) - mapped_inner_color = ( 0, 255, 0) - - def __init__(self, key_name, key_sym, top, left, width = 48, height = 48, disabled = False): - self.key_name = key_name - self.key_sym = key_sym - - if isinstance(top, str): - self.top = self.row_positions[top] - else: - self.top = top - - self.left = left - self.width = width - self.height = height - - self.bottom = self.top + self.height - self.right = self.left + self.width - - self.rect = (self.left, self.top, self.right, self.bottom) - self.position = (self.left, self.top) - - if disabled: - self.outer_color = self.lighter_outer_color - self.linewidth = 1 - else: - self.outer_color = self.default_outer_color - self.linewidth = 3 - - self.inner_color = self.default_inner_color - self.actions = [] - - def square(self): - if self.has_action(): - self.inner_color = self.mapped_inner_color - - return RoundedRect((0, 0, self.width, self.height), - self.outer_color, self.inner_color, self.linewidth) - - def collidepoint(self, position): - return self.surface.get_rect().collidepoint( - position[0] - self.position[0], - position[1] - self.position[1] - ) - - def draw(self, background_surface): - self.surface = self.square().surface() - - if getattr(sys, 'frozen', False): - police = font.Font(sys._MEIPASS + "/Ubuntu-Regular.ttf", 14) - else: - police = font.Font("Ubuntu-Regular.ttf", 14) - - text = police.render(self.key_sym, True, (0,0,0)) - self.surface.blit(text, (5,5)) - background_surface.blit(self.surface, self.position) - - def has_action(self): - return len(self.actions) > 0 - - def add_action(self, action_name, **arguments): - self.actions.append(Action(action_name, **arguments)) - - def next_action(self): - print("running next action") - - def do_actions(self): - self.current_action = 0 - print("running actions for {}".format(self.key_sym)) - if len(self.actions) > 0: - self.actions[0].run(self.next_action) - - def list_actions(self, surface): - print("bouh", self.key_sym) - surface.fill((255, 0, 0)) - - def find_by_key_num(key_num): - if key_num in Mapping.KEYS: - return Mapping.KEYS[key_num] - return None - - def find_by_collidepoint(position): - for key in Mapping.KEYS: - if Mapping.KEYS[key].collidepoint(position): - return Mapping.KEYS[key] - return None - - def find_by_unicode(key_sym): - for key in Mapping.KEYS: - if Mapping.KEYS[key].key_sym == key_sym: - return Mapping.KEYS[key] - return None - -class Mapping: - KEYS = { - K_ESCAPE: Key(K_ESCAPE, 'ESC', 'first', 0), - - K_F1: Key(K_F1, 'F1', 'first', 100), - K_F2: Key(K_F2, 'F2', 'first', 150), - K_F3: Key(K_F3, 'F3', 'first', 200), - K_F4: Key(K_F4, 'F4', 'first', 250), - - K_F5: Key(K_F5, 'F5', 'first', 325), - K_F6: Key(K_F6, 'F6', 'first', 375), - K_F7: Key(K_F7, 'F7', 'first', 425), - K_F8: Key(K_F8, 'F8', 'first', 475), - - K_F9: Key(K_F9, 'F9', 'first', 550), - K_F10: Key(K_F10, 'F10', 'first', 600), - K_F11: Key(K_F11, 'F11', 'first', 650), - K_F12: Key(K_F12, 'F12', 'first', 700), - - - 178: Key(178, '²', 'second', 0), - K_AMPERSAND: Key(K_AMPERSAND, '&', 'second', 50), - 233: Key(233, 'é', 'second', 100), - K_QUOTEDBL: Key(K_QUOTEDBL, '"', 'second', 150), - K_QUOTE: Key(K_QUOTE, "'", 'second', 200), - K_LEFTPAREN: Key(K_LEFTPAREN, '(', 'second', 250), - K_MINUS: Key(K_MINUS, '-', 'second', 300), - 232: Key(232, 'è', 'second', 350), - K_UNDERSCORE: Key(K_UNDERSCORE, '_', 'second', 400), - 231: Key(231, 'ç', 'second', 450), - 224: Key(224, 'à', 'second', 500), - K_RIGHTPAREN: Key(K_RIGHTPAREN, ')', 'second', 550), - K_EQUALS: Key(K_EQUALS, '=', 'second', 600), - - K_BACKSPACE: Key(K_BACKSPACE, '<-', 'second', 650, width = 98), - - - K_TAB: Key(K_TAB, 'tab', 'third', 0, width = 73), - K_a: Key(K_a, 'a', 'third', 75), - K_z: Key(K_z, 'z', 'third', 125), - K_e: Key(K_e, 'e', 'third', 175), - K_r: Key(K_r, 'r', 'third', 225), - K_t: Key(K_t, 't', 'third', 275), - K_y: Key(K_y, 'y', 'third', 325), - K_u: Key(K_u, 'u', 'third', 375), - K_i: Key(K_i, 'i', 'third', 425), - K_o: Key(K_o, 'o', 'third', 475), - K_p: Key(K_p, 'p', 'third', 525), - K_CARET: Key(K_CARET, '^', 'third', 575), - K_DOLLAR: Key(K_DOLLAR, '$', 'third', 625), - - K_RETURN: Key(K_RETURN, 'Enter', 'third', 692, width = 56, height = 98), - - K_CAPSLOCK: Key(K_CAPSLOCK, 'CAPS', 'fourth', 0, width = 88, disabled = True), - - K_q: Key(K_q, 'q', 'fourth', 90), - K_s: Key(K_s, 's', 'fourth', 140), - K_d: Key(K_d, 'd', 'fourth', 190), - K_f: Key(K_f, 'f', 'fourth', 240), - K_g: Key(K_g, 'g', 'fourth', 290), - K_h: Key(K_h, 'h', 'fourth', 340), - K_j: Key(K_j, 'j', 'fourth', 390), - K_k: Key(K_k, 'k', 'fourth', 440), - K_l: Key(K_l, 'l', 'fourth', 490), - K_m: Key(K_m, 'm', 'fourth', 540), - 249: Key(249, 'ù', 'fourth', 590), - K_ASTERISK: Key(K_ASTERISK, '*', 'fourth', 640), - - - K_LSHIFT: Key(K_LSHIFT, 'LShift', 'fifth', 0, width = 63, disabled = True), - - K_LESS: Key(K_LESS, '<', 'fifth', 65), - K_w: Key(K_w, 'w', 'fifth', 115), - K_x: Key(K_x, 'x', 'fifth', 165), - K_c: Key(K_c, 'c', 'fifth', 215), - K_v: Key(K_v, 'v', 'fifth', 265), - K_b: Key(K_b, 'b', 'fifth', 315), - K_n: Key(K_n, 'n', 'fifth', 365), - K_COMMA: Key(K_COMMA, ',', 'fifth', 415), - K_SEMICOLON: Key(K_SEMICOLON, ';', 'fifth', 465), - K_COLON: Key(K_COLON, ':', 'fifth', 515), - K_EXCLAIM: Key(K_EXCLAIM, '!', 'fifth', 565), - - K_RSHIFT: Key(K_RSHIFT, 'RShift', 'fifth', 615, width = 133, disabled = True), - - K_LCTRL: Key(K_LCTRL, 'LCtrl', 'sixth', 0, width = 63, disabled = True), - K_LSUPER: Key(K_LSUPER, 'LSuper', 'sixth', 115, disabled = True), - K_LALT: Key(K_LALT, 'LAlt', 'sixth', 165, disabled = True), - K_SPACE: Key(K_SPACE, 'Espace', 'sixth', 215, width = 248), - K_MODE: Key(K_MODE, 'AltGr', 'sixth', 465, disabled = True), - 314: Key(314, 'Compose', 'sixth', 515, disabled = True), - K_RCTRL: Key(K_RCTRL, 'RCtrl', 'sixth', 565, width = 63, disabled = True), - - - K_INSERT: Key(K_INSERT, 'ins', 'second', 755), - K_HOME: Key(K_HOME, 'home', 'second', 805), - K_PAGEUP: Key(K_PAGEUP, 'pg_u', 'second', 855), - K_DELETE: Key(K_DELETE, 'del', 'third', 755), - K_END: Key(K_END, 'end', 'third', 805), - K_PAGEDOWN: Key(K_PAGEDOWN, 'pg_d', 'third', 855), - - - K_UP: Key(K_UP, 'up', 'fifth', 805), - K_DOWN: Key(K_DOWN, 'down', 'sixth', 805), - K_LEFT: Key(K_LEFT, 'left', 'sixth', 755), - K_RIGHT: Key(K_RIGHT, 'right', 'sixth', 855), - } - -class MusicFile: - def __init__(self, filename): - self.filename = filename - -class RoundedRect: - def __init__(self, rect, outer_color, inner_color, linewidth = 2, radius = 0.4): - self.rect = Rect(rect) - self.outer_color = Color(*outer_color) - self.inner_color = Color(*inner_color) - self.linewidth = linewidth - self.radius = radius - - def surface(self): - rectangle = self.filledRoundedRect(self.rect, self.outer_color, self.radius) - - inner_rect = Rect(( - self.rect.left + 2 * self.linewidth, - self.rect.top + 2 * self.linewidth, - self.rect.right - 2 * self.linewidth, - self.rect.bottom - 2 * self.linewidth - )) - - inner_rectangle = self.filledRoundedRect(inner_rect, self.inner_color, self.radius) - - rectangle.blit(inner_rectangle, (self.linewidth, self.linewidth)) - - return rectangle - - def filledRoundedRect(self, rect, color, radius=0.4): - """ - filledRoundedRect(rect,color,radius=0.4) - - rect : rectangle - color : rgb or rgba - radius : 0 <= radius <= 1 - """ - - alpha = color.a - color.a = 0 - pos = rect.topleft - rect.topleft = 0,0 - rectangle = Surface(rect.size,SRCALPHA) - - circle = Surface([min(rect.size)*3]*2,SRCALPHA) - draw.ellipse(circle,(0,0,0),circle.get_rect(),0) - circle = transform.smoothscale(circle,[int(min(rect.size)*radius)]*2) - - radius = rectangle.blit(circle,(0,0)) - radius.bottomright = rect.bottomright - rectangle.blit(circle,radius) - radius.topright = rect.topright - rectangle.blit(circle,radius) - radius.bottomleft = rect.bottomleft - rectangle.blit(circle,radius) - - rectangle.fill((0,0,0),rect.inflate(-radius.w,0)) - rectangle.fill((0,0,0),rect.inflate(0,-radius.h)) - - rectangle.fill(color,special_flags=BLEND_RGBA_MAX) - rectangle.fill((255,255,255,alpha),special_flags=BLEND_RGBA_MIN) - - return rectangle - - -def parse_config(): - import yaml - stream = open("config.yml", "r") - config = yaml.load(stream) - stream.close() - - aliases = config['aliases'] - seen_files = {} - - for mapped_key in config['keys']: - key = Key.find_by_unicode(mapped_key) - if key is None: - continue - - for action in config['keys'][mapped_key]: - action_name = list(action)[0] - action_args = {} - if action[action_name] is None: - action[action_name] = [] - - for argument in action[action_name]: - if argument == 'include': - included = action[action_name]['include'] - if isinstance(included, str): - action_args.update(aliases[included]) - else: - for included_ in included: - action_args.update(aliases[included_]) - elif argument == 'file': - filename = action[action_name]['file'] - if filename not in seen_files: - seen_files[filename] = MusicFile.new(filename) - - action_args['music'] = seen_files[filename] - - else: - action_args[argument] = action[action_name][argument] - - key.add_action(action_name, **action_args) +import os +import math +import sounddevice as sd +import logging + +class Config: + pass + +def path(): + if getattr(sys, 'frozen', False): + return sys._MEIPASS + "/" + else: + path = os.path.dirname(os.path.realpath(__file__)) + return path + "/../" + +def parse_args(): + argv = sys.argv[1 :] + sys.argv = sys.argv[: 1] + if "--" in argv: + index = argv.index("--") + kivy_args = argv[index+1 :] + argv = argv[: index] + + sys.argv.extend(kivy_args) + + parser = argparse.ArgumentParser( + description="A Music Sampler application.", + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser.add_argument("-c", "--config", + default="config.yml", + required=False, + help="Config file to load") + parser.add_argument("-d", "--debug", + nargs=0, + action=DebugModeAction, + help="Print messages in console") + parser.add_argument("-m", "--builtin-mixing", + action="store_true", + help="Make the mixing of sounds manually\ + (do it if the system cannot handle it correctly)") + parser.add_argument("-l", "--latency", + default="high", + required=False, + help="Latency: low, high or number of seconds") + parser.add_argument("-b", "--blocksize", + default=0, + type=int, + required=False, + help="Blocksize: If not 0, the number of frames to take\ + at each step for the mixer") + parser.add_argument("-f", "--frame-rate", + default=44100, + type=int, + required=False, + help="Frame rate to play the musics") + parser.add_argument("-x", "--channels", + default=2, + type=int, + required=False, + help="Number of channels to use") + parser.add_argument("-s", "--sample-width", + default=2, + type=int, + required=False, + help="Sample width (number of bytes for each frame)") + parser.add_argument("-V", "--version", + action="version", + help="Displays the current version and exits. Only use\ + in bundled package", + version=show_version()) + parser.add_argument("--device", + action=SelectDeviceAction, + help="Select this sound device" + ) + parser.add_argument("--list-devices", + nargs=0, + action=ListDevicesAction, + help="List available sound devices" + ) + parser.add_argument('--', + dest="args", + help="Kivy arguments. All arguments after this are interpreted\ + by Kivy. Pass \"-- --help\" to get Kivy's usage.") + + from kivy.logger import Logger + Logger.setLevel(logging.WARN) + + args = parser.parse_args(argv) + + Config.yml_file = args.config + + Config.latency = args.latency + Config.blocksize = args.blocksize + Config.frame_rate = args.frame_rate + Config.channels = args.channels + Config.sample_width = args.sample_width + Config.builtin_mixing = args.builtin_mixing + +class DebugModeAction(argparse.Action): + def __call__(self, parser, namespace, values, option_string=None): + from kivy.logger import Logger + Logger.setLevel(logging.DEBUG) + +class SelectDeviceAction(argparse.Action): + def __call__(self, parser, namespace, values, option_string=None): + sd.default.device = values + +class ListDevicesAction(argparse.Action): + nargs = 0 + def __call__(self, parser, namespace, values, option_string=None): + print(sd.query_devices()) + sys.exit() + +def show_version(): + if getattr(sys, 'frozen', False): + with open(path() + ".pyinstaller_commit", "r") as f: + return f.read() + else: + return "option '-v' can only be used in bundled package" + +def duration_to_min_sec(duration): + minutes = int(duration / 60) + seconds = int(duration) % 60 + if minutes < 100: + return "{:2}:{:0>2}".format(minutes, seconds) + else: + return "{}:{:0>2}".format(minutes, seconds) + +def gain(volume, old_volume=None): + if old_volume is None: + return 20 * math.log10(max(volume, 0.1) / 100) + else: + return [ + 20 * math.log10(max(volume, 0.1) / max(old_volume, 0.1)), + max(volume, 0)] + +def debug_print(message, with_trace=False): + from kivy.logger import Logger + Logger.debug('MusicSampler: ' + message, exc_info=with_trace) + +def error_print(message, with_trace=False): + from kivy.logger import Logger + Logger.error('MusicSampler: ' + message, exc_info=with_trace) + +def warn_print(message, with_trace=False): + from kivy.logger import Logger + Logger.warn('MusicSampler: ' + message, exc_info=with_trace)