]>
Commit | Line | Data |
---|---|---|
6ebe6247 IB |
1 | # -*- coding: utf-8 -*- |
2 | import argparse | |
3 | import sys | |
4 | import os | |
5 | import math | |
6 | import sounddevice as sd | |
7 | import logging | |
6a327173 | 8 | import gettext |
16847231 | 9 | import yaml |
6a327173 | 10 | gettext.install('music_sampler') |
023d9381 | 11 | Logger = logging.getLogger("kivy") |
6ebe6247 IB |
12 | |
13 | from . import sysfont | |
14 | ||
15 | class Config: | |
16 | pass | |
17 | ||
18 | def find_font(name, style=sysfont.STYLE_NONE): | |
19 | if getattr(sys, 'frozen', False): | |
20 | font = sys._MEIPASS + "/fonts/{}_{}.ttf".format(name, style) | |
21 | else: | |
22 | font = sysfont.get_font(name, style=style) | |
23 | if font is not None: | |
24 | font = font[4] | |
25 | return font | |
26 | ||
27 | def register_fonts(): | |
28 | from kivy.core.text import LabelBase | |
29 | ||
30 | ubuntu_regular = find_font("Ubuntu", style=sysfont.STYLE_NORMAL) | |
31 | ubuntu_bold = find_font("Ubuntu", style=sysfont.STYLE_BOLD) | |
32 | symbola = find_font("Symbola") | |
33 | ||
34 | if ubuntu_regular is None: | |
2010311b IB |
35 | error_print("Font Ubuntu regular could not be found, " |
36 | "please install it.", exit=True) | |
6ebe6247 | 37 | if symbola is None: |
2010311b IB |
38 | error_print("Font Symbola could not be found, please install it.", |
39 | exit=True) | |
6ebe6247 IB |
40 | if ubuntu_bold is None: |
41 | warn_print("Font Ubuntu Bold could not be found.") | |
42 | ||
43 | LabelBase.register(name="Ubuntu", | |
44 | fn_regular=ubuntu_regular, | |
45 | fn_bold=ubuntu_bold) | |
46 | LabelBase.register(name="Symbola", | |
47 | fn_regular=symbola) | |
48 | ||
49 | ||
50 | def path(): | |
51 | if getattr(sys, 'frozen', False): | |
52 | return sys._MEIPASS + "/" | |
53 | else: | |
54 | return os.path.dirname(os.path.realpath(__file__)) | |
55 | ||
16847231 IB |
56 | |
57 | Configs = { | |
58 | 'music_path': { | |
59 | 'abbr': '-p', | |
60 | 'default': '.', | |
61 | 'help': _("Folder in which to find the music files"), | |
62 | 'type': None | |
63 | }, | |
64 | 'latency': { | |
65 | 'abbr': '-l', | |
66 | 'default': 'high', | |
67 | 'help': _("Latency: low, high or number of seconds"), | |
68 | 'type': None | |
69 | }, | |
70 | 'language': { | |
71 | 'abbr': '-L', | |
72 | 'default': "fr", | |
73 | 'help': _("Select another language"), | |
74 | 'type': None | |
75 | }, | |
76 | 'device': { | |
77 | 'abbr': '-d', | |
78 | 'default': None, | |
79 | 'help': _("Select this sound device"), | |
80 | 'type': None | |
81 | }, | |
82 | 'blocksize': { | |
83 | 'abbr': '-b', | |
84 | 'default': 0, | |
85 | 'help': _("Blocksize: If not 0, the number of frames to take\ | |
86 | at each step for the mixer"), | |
87 | 'type': int | |
88 | }, | |
89 | 'frame_rate': { | |
90 | 'abbr': '-f', | |
91 | 'default': 44100, | |
92 | 'help': _("Frame rate to play the musics"), | |
93 | 'type': int | |
94 | }, | |
95 | 'channels': { | |
96 | 'abbr': '-x', | |
97 | 'default': 2, | |
98 | 'help': _("Number of channels to use"), | |
99 | 'type': int | |
100 | }, | |
101 | 'sample_width': { | |
102 | 'abbr': '-s', | |
103 | 'default': 2, | |
104 | 'help': _("Sample width (number of bytes for each frame)"), | |
105 | 'type': int | |
106 | }, | |
107 | 'builtin_mixing': { | |
108 | 'default': False, | |
109 | 'help_yes': _("Make the mixing of sounds manually\ | |
110 | (do it if the system cannot handle it correctly)"), | |
111 | 'help_no': _("Don't make the mixing of sounds manually (default)"), | |
112 | 'type': 'boolean' | |
113 | }, | |
114 | 'debug': { | |
115 | 'abbr': '-d', | |
116 | 'default': False, | |
117 | 'help_yes': _("Print messages in console"), | |
118 | 'help_no': _("Don't print messages in console (default)"), | |
119 | 'type': 'boolean' | |
120 | }, | |
121 | 'focus_warning': { | |
122 | 'default': True, | |
123 | 'help_yes': _("Show a warning when focus is lost (default)"), | |
124 | 'help_no': _("Don't show warning when focus is lost"), | |
125 | 'type': 'boolean' | |
126 | }, | |
127 | 'list_devices': { | |
128 | 'help': _("List available sound devices"), | |
129 | 'type': 'action' | |
130 | }, | |
131 | } | |
132 | Configs_order = [ | |
133 | 'debug', | |
134 | 'music_path', | |
135 | 'builtin_mixing', | |
136 | 'latency', | |
137 | 'blocksize', | |
138 | 'frame_rate', | |
139 | 'channels', | |
140 | 'sample_width', | |
141 | 'focus_warning', | |
142 | 'language', | |
143 | 'list_devices', | |
144 | 'device', | |
145 | ] | |
6ebe6247 IB |
146 | def parse_args(): |
147 | argv = sys.argv[1 :] | |
148 | sys.argv = sys.argv[: 1] | |
149 | if "--" in argv: | |
150 | index = argv.index("--") | |
151 | kivy_args = argv[index+1 :] | |
152 | argv = argv[: index] | |
153 | ||
154 | sys.argv.extend(kivy_args) | |
155 | ||
023d9381 IB |
156 | os.environ["KIVY_NO_CONFIG"] = 'true' |
157 | sys.argv.extend(["-c", "kivy:log_level:warning"]) | |
158 | sys.argv.extend(["-c", "kivy:log_dir:/tmp"]) | |
159 | sys.argv.extend(["-c", "kivy:log_name:/tmp/music_sampler_%_.txt"]) | |
160 | ||
6ebe6247 | 161 | parser = argparse.ArgumentParser( |
16847231 IB |
162 | argument_default=argparse.SUPPRESS, |
163 | description=_("A Music Sampler application.")) | |
6ebe6247 IB |
164 | parser.add_argument("-V", "--version", |
165 | action="version", | |
6a327173 IB |
166 | help=_("Displays the current version and exits. Only use\ |
167 | in bundled package"), | |
6ebe6247 | 168 | version=show_version()) |
16847231 IB |
169 | parser.add_argument("-c", "--config", |
170 | default="config.yml", | |
6a327173 | 171 | required=False, |
16847231 IB |
172 | help=_("Config file to load (default: config.yml)")) |
173 | for argument in Configs_order: | |
174 | arg = Configs[argument] | |
175 | if arg['type'] != 'boolean' and arg['type'] != 'action': | |
176 | parser.add_argument(arg['abbr'], '--' + argument.replace('_', '-'), | |
177 | type=arg['type'], | |
178 | help=arg['help']+_(" (default: {})").format(arg['default'])) | |
179 | elif arg['type'] == 'boolean': | |
180 | parser.add_argument('--' + argument.replace('_', '-'), | |
181 | action='store_const', const=True, | |
182 | help=arg['help_yes']) | |
183 | parser.add_argument('--no-' + argument.replace('_', '-'), | |
184 | action='store_const', const=True, | |
185 | help=arg['help_no']) | |
186 | else: | |
187 | parser.add_argument('--' + argument.replace('_', '-'), | |
188 | action='store_const', const=True, | |
189 | help=arg['help']) | |
6ebe6247 IB |
190 | parser.add_argument('--', |
191 | dest="args", | |
6a327173 IB |
192 | help=_("Kivy arguments. All arguments after this are interpreted\ |
193 | by Kivy. Pass \"-- --help\" to get Kivy's usage.")) | |
6ebe6247 | 194 | |
6ebe6247 IB |
195 | args = parser.parse_args(argv) |
196 | ||
197 | Config.yml_file = args.config | |
16847231 | 198 | build_config(args) |
6ebe6247 | 199 | |
16847231 IB |
200 | if Config.device is not None: |
201 | sd.default.device = Config.device | |
202 | ||
203 | if Config.list_devices: | |
204 | print(sd.query_devices()) | |
205 | sys.exit() | |
206 | ||
207 | if Config.debug: | |
208 | sys.argv.extend(["-c", "kivy:log_level:debug"]) | |
209 | ||
210 | if Config.language != 'en': | |
6a327173 IB |
211 | gettext.translation("music_sampler", |
212 | localedir=path() + '/locales', | |
16847231 IB |
213 | languages=[Config.language]).install() |
214 | if not Config.music_path.endswith("/"): | |
215 | Config.music_path = Config.music_path + "/" | |
6ebe6247 | 216 | |
5e0dc4b2 IB |
217 | def dump_config(): |
218 | max_size = max(max(map(len, Configs_order)), len('config')) | |
219 | info_print("{:<{}} : {}".format( | |
220 | "config", max_size, Config.yml_file)) | |
221 | for item in Config.__dict__: | |
222 | if item in Configs_order: | |
223 | info_print("{:<{}} : {}".format( | |
224 | item, max_size, getattr(Config, item))) | |
225 | ||
16847231 IB |
226 | def build_config(args): |
227 | stream = open(Config.yml_file, "r") | |
228 | try: | |
229 | config = yaml.safe_load(stream) | |
230 | except Exception as e: | |
231 | error_print("Error while loading config file: {}".format(e)) | |
232 | config = {} | |
233 | stream.close() | |
234 | if 'config' in config: | |
235 | config = config['config'] | |
236 | else: | |
237 | config = {} | |
6ebe6247 | 238 | |
16847231 IB |
239 | for config_item in Configs_order: |
240 | if Configs[config_item]['type'] != 'boolean' and \ | |
241 | Configs[config_item]['type'] != 'action': | |
242 | t = Configs[config_item]['type'] or str | |
243 | if hasattr(args, config_item): | |
244 | setattr(Config, config_item, getattr(args, config_item)) | |
245 | elif config_item in config: | |
246 | setattr(Config, config_item, t(config[config_item])) | |
247 | else: | |
248 | setattr(Config, config_item, Configs[config_item]['default']) | |
249 | elif Configs[config_item]['type'] == 'boolean': | |
250 | if hasattr(args, 'no_' + config_item) or hasattr(args, config_item): | |
251 | setattr(Config, config_item, hasattr(args, config_item)) | |
252 | elif config_item in config: | |
253 | setattr(Config, config_item, config[config_item]) | |
254 | else: | |
255 | setattr(Config, config_item, Configs[config_item]['default']) | |
256 | else: | |
257 | setattr(Config, config_item, hasattr(args, config_item)) | |
6ebe6247 | 258 | |
6ebe6247 IB |
259 | |
260 | def show_version(): | |
261 | if getattr(sys, 'frozen', False): | |
262 | with open(path() + ".pyinstaller_commit", "r") as f: | |
263 | return f.read() | |
264 | else: | |
6a327173 | 265 | return _("option '-V' can only be used in bundled package") |
6ebe6247 IB |
266 | |
267 | def duration_to_min_sec(duration): | |
268 | minutes = int(duration / 60) | |
269 | seconds = int(duration) % 60 | |
270 | if minutes < 100: | |
271 | return "{:2}:{:0>2}".format(minutes, seconds) | |
272 | else: | |
273 | return "{}:{:0>2}".format(minutes, seconds) | |
274 | ||
275 | def gain(volume, old_volume=None): | |
276 | if old_volume is None: | |
277 | return 20 * math.log10(max(volume, 0.1) / 100) | |
278 | else: | |
279 | return [ | |
280 | 20 * math.log10(max(volume, 0.1) / max(old_volume, 0.1)), | |
281 | max(volume, 0)] | |
282 | ||
2010311b IB |
283 | def debug_print(message, with_trace=None): |
284 | if with_trace is None: | |
285 | with_trace = (Logger.getEffectiveLevel() < logging.WARN) | |
286 | with_trace &= (sys.exc_info()[0] is not None) | |
287 | ||
6ebe6247 IB |
288 | Logger.debug('MusicSampler: ' + message, exc_info=with_trace) |
289 | ||
2010311b IB |
290 | def error_print(message, exit=False, with_trace=None): |
291 | if with_trace is None: | |
292 | with_trace = (Logger.getEffectiveLevel() < logging.WARN) | |
293 | with_trace &= (sys.exc_info()[0] is not None) | |
294 | ||
295 | # FIXME: handle it correctly when in a thread | |
296 | if exit: | |
297 | Logger.critical('MusicSampler: ' + message, exc_info=with_trace) | |
298 | sys.exit(1) | |
299 | else: | |
300 | Logger.error('MusicSampler: ' + message, exc_info=with_trace) | |
301 | ||
302 | def warn_print(message, with_trace=None): | |
303 | if with_trace is None: | |
304 | with_trace = (Logger.getEffectiveLevel() < logging.WARN) | |
305 | with_trace &= (sys.exc_info()[0] is not None) | |
6ebe6247 | 306 | |
6ebe6247 IB |
307 | Logger.warn('MusicSampler: ' + message, exc_info=with_trace) |
308 | ||
5e0dc4b2 IB |
309 | def info_print(message, with_trace=None): |
310 | if with_trace is None: | |
311 | with_trace = (Logger.getEffectiveLevel() < logging.WARN) | |
312 | with_trace &= (sys.exc_info()[0] is not None) | |
313 | ||
314 | Logger.info('MusicSampler: ' + message, exc_info=with_trace) | |
315 |