]> git.immae.eu Git - perso/Immae/Projets/Python/MusicSampler.git/blobdiff - music_sampler/helpers.py
Add config
[perso/Immae/Projets/Python/MusicSampler.git] / music_sampler / helpers.py
index 1788084b59100ce028aa31c36fb37bc81525830d..9eb1a950ca50fb8028e7b52dd1d601f0d2a908f7 100644 (file)
@@ -5,6 +5,10 @@ import os
 import math
 import sounddevice as sd
 import logging
+import gettext
+import yaml
+gettext.install('music_sampler')
+Logger = logging.getLogger("kivy")
 
 from . import sysfont
 
@@ -28,11 +32,11 @@ def register_fonts():
     symbola = find_font("Symbola")
 
     if ubuntu_regular is None:
-        error_print("Font Ubuntu regular could not be found, please install it.")
-        sys.exit()
+        error_print("Font Ubuntu regular could not be found, "
+                "please install it.", exit=True)
     if symbola is None:
-        error_print("Font Symbola could not be found, please install it.")
-        sys.exit()
+        error_print("Font Symbola could not be found, please install it.",
+                exit=True)
     if ubuntu_bold is None:
         warn_print("Font Ubuntu Bold could not be found.")
 
@@ -49,6 +53,104 @@ def path():
     else:
         return os.path.dirname(os.path.realpath(__file__))
 
+
+Configs = {
+    'music_path': {
+        'abbr': '-p',
+        'default': '.',
+        'help': _("Folder in which to find the music files"),
+        'type': None
+    },
+    'latency': {
+        'abbr': '-l',
+        'default': 'high',
+        'help': _("Latency: low, high or number of seconds"),
+        'type': None
+    },
+    'language': {
+        'abbr': '-L',
+        'default': "fr",
+        'help': _("Select another language"),
+        'type': None
+    },
+    'device': {
+        'abbr': '-d',
+        'default': None,
+        'help': _("Select this sound device"),
+        'type': None
+    },
+    'blocksize': {
+        'abbr': '-b',
+        'default': 0,
+        'help': _("Blocksize: If not 0, the number of frames to take\
+                    at each step for the mixer"),
+        'type': int
+    },
+    'frame_rate': {
+        'abbr': '-f',
+        'default': 44100,
+        'help': _("Frame rate to play the musics"),
+        'type': int
+    },
+    'channels': {
+        'abbr': '-x',
+        'default': 2,
+        'help': _("Number of channels to use"),
+        'type': int
+    },
+    'sample_width': {
+        'abbr': '-s',
+        'default': 2,
+        'help': _("Sample width (number of bytes for each frame)"),
+        'type': int
+    },
+    'builtin_mixing': {
+        'default': False,
+        'help_yes': _("Make the mixing of sounds manually\
+                    (do it if the system cannot handle it correctly)"),
+        'help_no': _("Don't make the mixing of sounds manually (default)"),
+        'type': 'boolean'
+    },
+    'debug': {
+        'abbr': '-d',
+        'default': False,
+        'help_yes': _("Print messages in console"),
+        'help_no': _("Don't print messages in console (default)"),
+        'type': 'boolean'
+    },
+    'focus_warning': {
+        'default': True,
+        'help_yes': _("Show a warning when focus is lost (default)"),
+        'help_no': _("Don't show warning when focus is lost"),
+        'type': 'boolean'
+    },
+    'load_all_musics': {
+        'default': True,
+        'help_yes': _("Load all the musics at launch time (default)"),
+        'help_no': _("Don't load all the musics at launch time (use it if you \
+            have memory problems)"),
+        'type': 'boolean'
+    },
+    'list_devices': {
+        'help': _("List available sound devices"),
+        'type': 'action'
+    },
+}
+Configs_order = [
+    'debug',
+    'music_path',
+    'builtin_mixing',
+    'latency',
+    'blocksize',
+    'frame_rate',
+    'channels',
+    'sample_width',
+    'focus_warning',
+    'language',
+    'list_devices',
+    'device',
+    'load_all_musics',
+]
 def parse_args():
     argv = sys.argv[1 :]
     sys.argv = sys.argv[: 1]
@@ -59,108 +161,116 @@ def parse_args():
 
         sys.argv.extend(kivy_args)
 
+    os.environ["KIVY_NO_CONFIG"] = 'true'
+    sys.argv.extend(["-c", "kivy:log_level:warning"])
+    sys.argv.extend(["-c", "kivy:log_dir:/tmp"])
+    sys.argv.extend(["-c", "kivy:log_name:/tmp/music_sampler_%_.txt"])
+
     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("-p", "--music-path",
-            default=".",
-            required=False,
-            help="Folder in which to find the music files")
-    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)")
+            argument_default=argparse.SUPPRESS,
+            description=_("A Music Sampler application."))
     parser.add_argument("-V", "--version",
             action="version",
-            help="Displays the current version and exits. Only use\
-                    in bundled package",
+            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("-c", "--config",
+            default="config.yml",
+            required=False,
+            help=_("Config file to load (default: config.yml)"))
+    for argument in Configs_order:
+        arg = Configs[argument]
+        if arg['type'] != 'boolean' and arg['type'] != 'action':
+            parser.add_argument(arg['abbr'], '--' + argument.replace('_', '-'),
+                    type=arg['type'],
+                    help=arg['help']+_(" (default: {})").format(arg['default']))
+        elif arg['type'] == 'boolean':
+            parser.add_argument('--' + argument.replace('_', '-'),
+                    action='store_const', const=True,
+                    help=arg['help_yes'])
+            parser.add_argument('--no-' + argument.replace('_', '-'),
+                    action='store_const', const=True,
+                    help=arg['help_no'])
+        else:
+            parser.add_argument('--' + argument.replace('_', '-'),
+                    action='store_const', const=True,
+                    help=arg['help'])
     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)
+            help=_("Kivy arguments. All arguments after this are interpreted\
+                    by Kivy. Pass \"-- --help\" to get Kivy's usage."))
 
     args = parser.parse_args(argv)
 
     Config.yml_file = args.config
+    build_config(args)
 
-    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
-    if args.music_path.endswith("/"):
-        Config.music_path = args.music_path
-    else:
-        Config.music_path = args.music_path + "/"
-
-class DebugModeAction(argparse.Action):
-    def __call__(self, parser, namespace, values, option_string=None):
-        from kivy.logger import Logger
-        Logger.setLevel(logging.DEBUG)
+    if Config.device is not None:
+        sd.default.device = Config.device
 
-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):
+    if Config.list_devices:
         print(sd.query_devices())
         sys.exit()
 
+    if Config.debug:
+        sys.argv.extend(["-c", "kivy:log_level:debug"])
+
+    if Config.language != 'en':
+        gettext.translation("music_sampler",
+                localedir=path() + '/locales',
+                languages=[Config.language]).install()
+    if not Config.music_path.endswith("/"):
+        Config.music_path = Config.music_path + "/"
+
+def dump_config():
+    max_size = max(max(map(len, Configs_order)), len('config'))
+    info_print("{:<{}} : {}".format(
+        "config", max_size, Config.yml_file))
+    for item in Config.__dict__:
+        if item in Configs_order:
+            info_print("{:<{}} : {}".format(
+                item, max_size, getattr(Config, item)))
+
+def build_config(args):
+    stream = open(Config.yml_file, "r", encoding='utf8')
+    try:
+        config = yaml.safe_load(stream)
+    except Exception as e:
+        error_print("Error while loading config file: {}".format(e))
+        config = {}
+    stream.close()
+    if 'config' in config:
+        config = config['config']
+    else:
+        config = {}
+
+    for config_item in Configs_order:
+        if Configs[config_item]['type'] != 'boolean' and \
+                Configs[config_item]['type'] != 'action':
+            t = Configs[config_item]['type'] or str
+            if hasattr(args, config_item):
+                setattr(Config, config_item, getattr(args, config_item))
+            elif config_item in config:
+                setattr(Config, config_item, t(config[config_item]))
+            else:
+                setattr(Config, config_item, Configs[config_item]['default'])
+        elif Configs[config_item]['type'] == 'boolean':
+            if hasattr(args, 'no_' + config_item) or hasattr(args, config_item):
+                setattr(Config, config_item, hasattr(args, config_item))
+            elif config_item in config:
+                setattr(Config, config_item, config[config_item])
+            else:
+                setattr(Config, config_item, Configs[config_item]['default'])
+        else:
+            setattr(Config, config_item, hasattr(args, config_item))
+
+
 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"
+        return _("option '-V' can only be used in bundled package")
 
 def duration_to_min_sec(duration):
     minutes = int(duration / 60)
@@ -178,15 +288,36 @@ def gain(volume, old_volume=None):
                 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
+def debug_print(message, with_trace=None):
+    if with_trace is None:
+        with_trace = (Logger.getEffectiveLevel() < logging.WARN)
+    with_trace &= (sys.exc_info()[0] is not None)
+
     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 error_print(message, exit=False, with_trace=None):
+    if with_trace is None:
+        with_trace = (Logger.getEffectiveLevel() < logging.WARN)
+    with_trace &= (sys.exc_info()[0] is not None)
+
+    # FIXME: handle it correctly when in a thread
+    if exit:
+        Logger.critical('MusicSampler: ' + message, exc_info=with_trace)
+        sys.exit(1)
+    else:
+        Logger.error('MusicSampler: ' + message, exc_info=with_trace)
+
+def warn_print(message, with_trace=None):
+    if with_trace is None:
+        with_trace = (Logger.getEffectiveLevel() < logging.WARN)
+    with_trace &= (sys.exc_info()[0] is not None)
 
-def warn_print(message, with_trace=False):
-    from kivy.logger import Logger
     Logger.warn('MusicSampler: ' + message, exc_info=with_trace)
 
+def info_print(message, with_trace=None):
+    if with_trace is None:
+        with_trace = (Logger.getEffectiveLevel() < logging.WARN)
+    with_trace &= (sys.exc_info()[0] is not None)
+
+    Logger.info('MusicSampler: ' + message, exc_info=with_trace)
+