]> git.immae.eu Git - perso/Immae/Projets/Python/MusicSampler.git/blobdiff - helpers/__init__.py
Coding styles
[perso/Immae/Projets/Python/MusicSampler.git] / helpers / __init__.py
index c42b378817aa26e8898b2920e0d2989048494e1e..9d6663893bc32634b1163f7a525b8b04a7bdf9a5 100644 (file)
 # -*- coding: utf-8 -*-
-from pygame import *
-
-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()
-
-        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 argparse
+import sys
+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.ERROR)
+
+    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(volume / 100)
+    else:
+        return [
+                20 * math.log10(max(volume, 0.1) / max(old_volume, 0.1)),
+                max(volume, 0)]
+
+def debug_print(message):
+    from kivy.logger import Logger
+    Logger.debug('MusicSampler: ' + message)
+
+def error_print(message):
+    from kivy.logger import Logger
+    Logger.error('MusicSampler: ' + message)