]> git.immae.eu Git - perso/Immae/Projets/Python/MusicSampler.git/blob - music_sampler/helpers.py
2249746a5a928cf2c416915a0b587c6b10b704d8
[perso/Immae/Projets/Python/MusicSampler.git] / music_sampler / helpers.py
1 # -*- coding: utf-8 -*-
2 import argparse
3 import sys
4 import os
5 import math
6 import sounddevice as sd
7 import logging
8 Logger = logging.getLogger("kivy")
9
10 from . import sysfont
11
12 class Config:
13 pass
14
15 def find_font(name, style=sysfont.STYLE_NONE):
16 if getattr(sys, 'frozen', False):
17 font = sys._MEIPASS + "/fonts/{}_{}.ttf".format(name, style)
18 else:
19 font = sysfont.get_font(name, style=style)
20 if font is not None:
21 font = font[4]
22 return font
23
24 def register_fonts():
25 from kivy.core.text import LabelBase
26
27 ubuntu_regular = find_font("Ubuntu", style=sysfont.STYLE_NORMAL)
28 ubuntu_bold = find_font("Ubuntu", style=sysfont.STYLE_BOLD)
29 symbola = find_font("Symbola")
30
31 if ubuntu_regular is None:
32 error_print("Font Ubuntu regular could not be found, "
33 "please install it.", exit=True)
34 if symbola is None:
35 error_print("Font Symbola could not be found, please install it.",
36 exit=True)
37 if ubuntu_bold is None:
38 warn_print("Font Ubuntu Bold could not be found.")
39
40 LabelBase.register(name="Ubuntu",
41 fn_regular=ubuntu_regular,
42 fn_bold=ubuntu_bold)
43 LabelBase.register(name="Symbola",
44 fn_regular=symbola)
45
46
47 def path():
48 if getattr(sys, 'frozen', False):
49 return sys._MEIPASS + "/"
50 else:
51 return os.path.dirname(os.path.realpath(__file__))
52
53 def parse_args():
54 argv = sys.argv[1 :]
55 sys.argv = sys.argv[: 1]
56 if "--" in argv:
57 index = argv.index("--")
58 kivy_args = argv[index+1 :]
59 argv = argv[: index]
60
61 sys.argv.extend(kivy_args)
62
63 os.environ["KIVY_NO_CONFIG"] = 'true'
64 sys.argv.extend(["-c", "kivy:log_level:warning"])
65 sys.argv.extend(["-c", "kivy:log_dir:/tmp"])
66 sys.argv.extend(["-c", "kivy:log_name:/tmp/music_sampler_%_.txt"])
67
68 parser = argparse.ArgumentParser(
69 description="A Music Sampler application.",
70 formatter_class=argparse.ArgumentDefaultsHelpFormatter)
71 parser.add_argument("-c", "--config",
72 default="config.yml",
73 required=False,
74 help="Config file to load")
75 parser.add_argument("-p", "--music-path",
76 default=".",
77 required=False,
78 help="Folder in which to find the music files")
79 parser.add_argument("-d", "--debug",
80 nargs=0,
81 action=DebugModeAction,
82 help="Print messages in console")
83 parser.add_argument("-m", "--builtin-mixing",
84 action="store_true",
85 help="Make the mixing of sounds manually\
86 (do it if the system cannot handle it correctly)")
87 parser.add_argument("-l", "--latency",
88 default="high",
89 required=False,
90 help="Latency: low, high or number of seconds")
91 parser.add_argument("-b", "--blocksize",
92 default=0,
93 type=int,
94 required=False,
95 help="Blocksize: If not 0, the number of frames to take\
96 at each step for the mixer")
97 parser.add_argument("-f", "--frame-rate",
98 default=44100,
99 type=int,
100 required=False,
101 help="Frame rate to play the musics")
102 parser.add_argument("-x", "--channels",
103 default=2,
104 type=int,
105 required=False,
106 help="Number of channels to use")
107 parser.add_argument("-s", "--sample-width",
108 default=2,
109 type=int,
110 required=False,
111 help="Sample width (number of bytes for each frame)")
112 parser.add_argument("-V", "--version",
113 action="version",
114 help="Displays the current version and exits. Only use\
115 in bundled package",
116 version=show_version())
117 parser.add_argument("--device",
118 action=SelectDeviceAction,
119 help="Select this sound device"
120 )
121 parser.add_argument("--list-devices",
122 nargs=0,
123 action=ListDevicesAction,
124 help="List available sound devices"
125 )
126 parser.add_argument("--no-focus-warning",
127 action='store_true',
128 help="Don't show warning when focus is lost"
129 )
130 parser.add_argument('--',
131 dest="args",
132 help="Kivy arguments. All arguments after this are interpreted\
133 by Kivy. Pass \"-- --help\" to get Kivy's usage.")
134
135 args = parser.parse_args(argv)
136
137 Config.yml_file = args.config
138
139 Config.latency = args.latency
140 Config.blocksize = args.blocksize
141 Config.frame_rate = args.frame_rate
142 Config.channels = args.channels
143 Config.sample_width = args.sample_width
144 Config.builtin_mixing = args.builtin_mixing
145 Config.no_focus_warning = args.no_focus_warning
146 if args.music_path.endswith("/"):
147 Config.music_path = args.music_path
148 else:
149 Config.music_path = args.music_path + "/"
150
151 class DebugModeAction(argparse.Action):
152 def __call__(self, parser, namespace, values, option_string=None):
153 sys.argv.extend(["-c", "kivy:log_level:debug"])
154
155 class SelectDeviceAction(argparse.Action):
156 def __call__(self, parser, namespace, values, option_string=None):
157 sd.default.device = values
158
159 class ListDevicesAction(argparse.Action):
160 nargs = 0
161 def __call__(self, parser, namespace, values, option_string=None):
162 print(sd.query_devices())
163 sys.exit()
164
165 def show_version():
166 if getattr(sys, 'frozen', False):
167 with open(path() + ".pyinstaller_commit", "r") as f:
168 return f.read()
169 else:
170 return "option '-v' can only be used in bundled package"
171
172 def duration_to_min_sec(duration):
173 minutes = int(duration / 60)
174 seconds = int(duration) % 60
175 if minutes < 100:
176 return "{:2}:{:0>2}".format(minutes, seconds)
177 else:
178 return "{}:{:0>2}".format(minutes, seconds)
179
180 def gain(volume, old_volume=None):
181 if old_volume is None:
182 return 20 * math.log10(max(volume, 0.1) / 100)
183 else:
184 return [
185 20 * math.log10(max(volume, 0.1) / max(old_volume, 0.1)),
186 max(volume, 0)]
187
188 def debug_print(message, with_trace=None):
189 if with_trace is None:
190 with_trace = (Logger.getEffectiveLevel() < logging.WARN)
191 with_trace &= (sys.exc_info()[0] is not None)
192
193 Logger.debug('MusicSampler: ' + message, exc_info=with_trace)
194
195 def error_print(message, exit=False, with_trace=None):
196 if with_trace is None:
197 with_trace = (Logger.getEffectiveLevel() < logging.WARN)
198 with_trace &= (sys.exc_info()[0] is not None)
199
200 # FIXME: handle it correctly when in a thread
201 if exit:
202 Logger.critical('MusicSampler: ' + message, exc_info=with_trace)
203 sys.exit(1)
204 else:
205 Logger.error('MusicSampler: ' + message, exc_info=with_trace)
206
207 def warn_print(message, with_trace=None):
208 if with_trace is None:
209 with_trace = (Logger.getEffectiveLevel() < logging.WARN)
210 with_trace &= (sys.exc_info()[0] is not None)
211
212 Logger.warn('MusicSampler: ' + message, exc_info=with_trace)
213