Cleanup: minimum-Y-extent removed, delete duplicate instr. names, remove instr_name_e...
[orchestrallily.git] / generate_oly_score.py
blob9319501087dd852d1641f00b8c4ba11b829e5ad6
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
4 import sys
5 import os
6 import os.path
7 import getopt
8 import re
9 import filecmp
10 import string
11 import subprocess
13 program_name = 'generate_oly_score'
14 settings_file = 'oly_structure.def'
15 output_dir = ''
16 script_path = os.path.dirname(__file__)
17 basename = ""
19 settings_files = []
20 src_files = []
21 score_files = []
22 tex_files = []
24 ######################################################################
25 # Options handling
26 ######################################################################
28 help_text = r"""Usage: %(program_name)s [OPTIONS]... [DEF-FILE]
29 Create a complete file structure for a score using OrchestralLily.
30 If no definitions file it given, the file oly_structure.def is used
32 Options:
33 -h, --help print this help
34 -o, --output=NAME write tely doc to NAME (default: read from settings or current directory)
35 """
37 def help (text):
38 sys.stdout.write ( text)
39 sys.exit (0)
41 (options, files) = getopt.getopt (sys.argv[1:], 'ho:',
42 ['help', 'output='])
44 for opt in options:
45 o = opt[0]
46 a = opt[1]
47 if o == '-h' or o == '--help':
48 help (help_text % vars ())
49 #elif o == '-s' or o == '--settings':
50 #settings_file = a
51 elif o == '-o' or o == '--output':
52 output_dir = a
53 else:
54 raise Exception ('unknown option: ' + o)
56 if files:
57 settings_file = files[0]
60 ######################################################################
61 # Settings loading
62 ######################################################################
64 def load_settings (file_name):
65 global output_dir, basename;
66 try:
67 in_f = open (file_name)
68 s = in_f.read()
69 in_f.close()
70 settings_expr = eval(s);
71 except IOError:
72 print ("Unable to load settings file '%s'. Exiting..." % file_name)
73 exit (-1);
74 except SyntaxError as ex:
75 print ex;
76 print ("Unable to interpret settings file '%s', it's syntax is invalid. Exiting..." % file_name);
77 exit (-1);
79 basename = settings_expr.get ("basename", "Score")
80 if not output_dir:
81 output_dir = settings_expr.get ("output_dir", output_dir) + "/";
82 return settings_expr;
85 ######################################################################
86 # File Writing
87 ######################################################################
89 def write_file (file_name, contents):
90 fn = file_name
91 if os.path.exists (file_name):
92 fn += ".new"
94 dir = os.path.dirname (fn);
95 if not os.path.exists (dir):
96 os.mkdir (dir)
98 try:
99 out_f = open (fn, "w")
100 s = out_f.write(contents)
101 out_f.close()
102 except IOError:
103 print ("Unable to write to output file '%s'. Exiting..." % fn)
104 exit (-1);
106 # If the file already existed, check if the new file is identical.
107 if (fn != file_name):
108 patchfile = os.path.join (dir, "patches", os.path.basename(file_name) + ".patch")
109 if os.path.exists (patchfile):
110 #print ("Trying to apply patch %s" % patchfile)
111 try:
112 #retcode = subprocess.call(["patch ", fn, patchfile], shell=True)
113 retcode = subprocess.call("patch -Ns \""+ fn+ "\" \"" + patchfile + "\"", shell=True)
114 if retcode < 0:
115 print >>sys.stderr, "Unable to apply patch to file \"", fn, "\"."
116 #else:
117 #print >>sys.stderr, "Child returned", retcode
118 except OSError, e:
119 print >>sys.stderr, "Execution failed:", e
120 #else:
121 #print ("Unable to find patch %s" % patchfile)
123 if filecmp.cmp (fn, file_name):
124 os.unlink (fn);
125 else:
126 print ("A file %s already existed, created new file %s." % (os.path.basename(file_name), os.path.basename(fn)))
130 ######################################################################
131 # Templates
132 ######################################################################
134 def init_replacements (settings):
135 rep = settings.copy ();
136 del rep["parts"];
137 del rep["instruments"];
138 del rep["lyrics"];
139 del rep["scores"];
141 return rep
144 ######################################################################
145 # Templates
146 ######################################################################
148 template_names = {
149 "Makefile": "Makefile",
151 "movement": "TEMPLATE_Include_Movement.ily",
152 "snippet_def_lyrics": "TEMPLATE_Snippet_DefinitionLyrics.ily",
153 "snippet_def_music": "TEMPLATE_Snippet_DefinitionMusic.ily",
154 "snippet_def_include": "TEMPLATE_Snippet_DefinitionInclude.ily",
156 "settings": "TEMPLATE_Settings.ily",
157 "settings_global": "TEMPLATE_Settings_Global.ily",
158 "settings_fullscore": "TEMPLATE_Settings_FullScore.ily",
159 "settings_instrument": "TEMPLATE_Settings_Instrument.ily",
160 "settings_vocalvoice": "TEMPLATE_Settings_VocalVoice.ily",
161 "settings_vocalscore": "TEMPLATE_Settings_VocalScore.ily",
163 "snippet_part_score": "TEMPLATE_Snippet_PartScore.ily",
165 "score_full": "TEMPLATE_Score_FullScore.ly",
166 "score_instrument": "TEMPLATE_Score_Instrument.ly",
168 "itex_settings": "TEXTEMPLATE_Settings.itex",
169 "itex_about": "TEXTEMPLATE_Include_About.itex",
170 "itex_contents": "TEXTEMPLATE_Include_ContentsMaterial.itex",
171 "itex_leben": "TEXTEMPLATE_Include_Leben.itex",
172 "itex_text": "TEXTEMPLATE_Include_Text.itex",
174 "itex_todo": "TEXTEMPLATE_Include_Todo.itex",
175 "itex_preface_long": "TEXTEMPLATE_Include_Preface_Long.itex",
176 "itex_preface_short": "TEXTEMPLATE_Include_Preface_Short.itex",
178 "itex_coverpage": "TEXTEMPLATE_Include_Coverpage.itex",
179 "itex_backpage": "TEXTEMPLATE_Include_Backpage.itex",
181 "itex_kritbericht": "TEXTEMPLATE_Include_KritBericht.itex",
182 "tex_score": "TEXTEMPLATE_Score.tex",
183 "tex_instruments": "TEXTEMPLATE_Score_Instruments.tex",
185 "itex_snippet_textmovement": "TEXTEMPLATE_Snippet_TextMovement.itex",
186 "itex_snippet_kritbericht": "TEXTEMPLATE_Snippet_KritBericht_Movement.itex",
187 "itex_snippet_material": "TEXTEMPLATE_Snippet_Material_Score.itex",
188 "itex_snippet_instrument": "TEXTEMPLATE_Snippet_Instrument.itex",
191 def load_file (file_name):
192 s = False;
193 try:
194 in_f = open (file_name, "rb")
195 s = in_f.read()
196 in_f.close()
197 except IOError:
198 print ("Unable to load file %s" % file_name);
199 return s
201 def load_templates (settings):
202 templates = {};
203 template_path = script_path + "/Templates/";
205 for id in template_names:
206 filename = template_names[id]
207 s = load_file (template_path + filename);
208 if s:
209 templates[id] = s
210 else:
211 print ("Unable to load template file %s (id %s).\n" % (filename, id));
212 return templates;
215 ######################################################################
216 # Creating settings files
217 ######################################################################
219 def write_settings_file (fn, contents):
220 write_file (fn, contents );
221 settings_files.append (fn)
223 def generate_settings_files (settings, templates, replacements):
224 inc = "";
225 for m in src_files:
226 replacements["filename"] = m
227 inc += (templates["snippet_def_include"] % replacements)
228 del replacements["filename"];
229 replacements["includefiles"] = inc
231 write_settings_file (output_dir + basename + "_Settings.ily", templates["settings"] % replacements );
232 write_file (output_dir + basename + "_Settings_Global.ily", templates["settings_global"] % replacements );
234 if (settings.get("scores", [])):
235 write_settings_file (output_dir + basename + "_Settings_FullScore.ily", templates["settings_fullscore"] % replacements );
236 if (settings.get("instruments", [])):
237 write_settings_file (output_dir + basename + "_Settings_Instrument.ily", templates["settings_instrument"] % replacements );
238 else:
239 print ("WARNING: No instruments: %s" % settings.get("instruments", []));
240 if (settings.get("lyrics", [])):
241 write_settings_file (output_dir + basename + "_Settings_VocalVoice.ily", templates["settings_vocalvoice"] % replacements );
242 write_settings_file (output_dir + basename + "_Settings_VocalScore.ily", templates["settings_vocalscore"] % replacements );
243 del replacements["includefiles"]
246 ######################################################################
247 # Creating movement files
248 ######################################################################
250 def generate_movement_files (settings, templates, replacements):
251 movements = settings.get ("parts", []);
252 instruments = settings.get ("instruments", []);
253 lyrics = settings.get ("lyrics", []);
254 if (movements == {}):
255 print ("No parts defined in settings file...");
256 exit (-1);
257 if (instruments == []):
258 print ("No instruments defined in settings file...");
259 exit (-1);
261 for m in movements:
262 filename_ext = ""
263 if type (m) == dict:
264 name = m.get ("name", "TODO")
265 filename_ext = m.get ("filename", name)
266 replacements["movement"] = name
267 replacements["movementname"] = m.get ("piece", name)
268 replacements["movementtacet"] = m.get ("piecetacet", replacements["movementname"] + " tacet")
269 elif type(m) == str:
270 filename_ext = m
271 replacements["movement"] = m
272 replacements["movementname"] = m
273 replacements["movementtacet"] = (m + " tacet")
274 else:
275 print ("Type of movement entry %s is not a dict or a string" % m)
276 continue
278 defs = ""
279 for i in instruments:
280 replacements["instrument"] = i
281 defs += (templates["snippet_def_music"] % replacements);
282 if (i in lyrics):
283 defs += (templates["snippet_def_lyrics"] % replacements);
284 del replacements["instrument"]
285 replacements["movementinstrumentdefinitions"] = defs
286 fn = basename + "_Music_" + filename_ext + ".ily"
287 write_file (output_dir + fn, templates["movement"] % replacements);
288 src_files.append (fn)
289 del replacements["movement"]
290 del replacements["movementname"]
291 del replacements["movementtacet"]
294 ######################################################################
295 # Creating instrumental score files
296 ######################################################################
298 def generate_instrument_files (settings, templates, replacements):
299 movements = settings.get ("parts", []);
300 instruments = settings.get ("instruments", []);
301 lyrics = settings.get ("lyrics", []);
303 for i in instruments:
304 replacements["instrument"] = i
305 defs = ""
306 for m in movements:
307 if type(m) == dict:
308 replacements["movement"] = m.get ("name", "TODO")
309 replacements["movementname"] = m.get ("piece", replacements["movement"])
310 else:
311 replacements["movement"] = m
312 replacements["movementname"] = m
313 defs += (templates["snippet_part_score"] % replacements);
314 del replacements["movement"]
315 del replacements["movementname"]
316 replacements["partlist"] = defs
317 if i in lyrics:
318 settings = "VocalVoice"
319 else:
320 settings = "Instrument"
321 replacements["settings"] = settings
322 fn = output_dir + basename + "_Instrument_" + i + ".ly"
323 write_file (fn, templates["score_instrument"] % replacements);
324 score_files.append (fn)
325 del replacements["instrument"]
326 del replacements["settings"]
328 ######################################################################
329 # Creating score files
330 ######################################################################
333 score_names = {
334 "ParticellScore": "Particell",
336 score_settings = {
337 "ChoralScore": "VocalScore",
338 "LongScore": "FullScore",
339 "OriginalScore": "FullScore",
340 "Particell": "FullScore"
343 def generate_score_files (settings, templates, replacements):
344 movements = settings.get ("parts",[]);
345 scores = settings.get ("scores", []);
347 for i in scores:
348 sn = (i + "Score")
349 fullsn = score_names.get (sn, sn);
350 replacements["score"] = fullsn
351 replacements["settings"] = score_settings.get (fullsn,fullsn)
352 replacements["instrument"] = replacements["score"]
353 defs = ""
354 for m in movements:
355 if type(m) == dict:
356 replacements["movement"] = m.get ("name", "TODO")
357 replacements["movementname"] = m.get ("piece", replacements["movement"])
358 else:
359 replacements["movement"] = m
360 replacements["movementname"] = m
361 defs += (templates["snippet_part_score"] % replacements);
362 del replacements["movement"]
363 del replacements["movementname"]
364 replacements["partlist"] = defs
365 filename = output_dir + basename + "_Score_" + i + ".ly";
366 if i == "":
367 filename = output_dir + basename + "_Score.ly"
368 write_file (filename, templates["score_full"] % replacements);
369 score_files.append (filename)
370 del replacements["score"]
371 del replacements["instrument"]
374 ######################################################################
375 # Creating LaTeX files
376 ######################################################################
378 def write_tex_file (fn, contents):
379 write_file (output_dir + "/"+ fn, contents );
380 tex_files.append (fn)
382 def generate_tex_files (settings, templates, replacements):
383 write_tex_file ("TeX_" + basename + "_Settings.itex", templates["itex_settings"] % replacements);
384 write_tex_file ("TeX_" + basename + "_Include_Coverpage.itex", templates["itex_coverpage"] % replacements);
385 write_tex_file ("TeX_" + basename + "_Include_Backpage.itex", templates["itex_backpage"] % replacements);
386 write_tex_file ("TeX_" + basename + "_Include_About.itex", templates["itex_about"] % replacements);
387 write_tex_file ("TeX_" + basename + "_Include_Leben.itex", templates["itex_leben"] % replacements);
388 write_tex_file ("TeX_" + basename + "_Include_Todo.itex", templates["itex_todo"] % replacements);
389 write_tex_file ("TeX_" + basename + "_Include_Preface_Long.itex", templates["itex_preface_long"] % replacements);
390 write_tex_file ("TeX_" + basename + "_Include_Preface_Short.itex", templates["itex_preface_short"] % replacements);
392 txt = ""
393 kb = ""
394 for m in settings.get ("parts",[]):
395 if type(m) == dict:
396 replacements["movement"] = m.get ("name", "TODO")
397 replacements["movementname"] = m.get ("piece", replacements["movement"])
398 else:
399 replacements["movement"] = m
400 replacements["movementname"] = m
401 txt += (templates["itex_snippet_textmovement"] % replacements);
402 kb += (templates["itex_snippet_kritbericht"] % replacements);
403 del replacements["movement"]
404 del replacements["movementname"]
405 replacements["movementtexts"] = txt
406 replacements["kritbericht_movements"] = kb
407 write_tex_file ("TeX_" + basename + "_Include_Text.itex", templates["itex_text"] % replacements);
408 write_tex_file ("TeX_" + basename + "_Include_KritBericht.itex", templates["itex_kritbericht"] % replacements);
409 del replacements["movementtexts"]
410 del replacements["kritbericht_movements"]
412 ### Contents and Material:
413 lst = ""
414 for i in settings.get ("scores", []):
415 replacements["type"] = i + "Score";
416 lst += templates["itex_snippet_material"] % replacements
417 replacements["type"] = "Instruments"
418 lst += templates["itex_snippet_material"] % replacements
419 for i in settings.get ("instruments", []):
420 replacements["type"] = i;
421 lst += templates["itex_snippet_material"] % replacements
422 del replacements["type"]
423 replacements["material_list"] = lst;
424 write_tex_file ("TeX_" + basename + "_Include_ContentsMaterial.itex", templates["itex_contents"] % replacements);
425 del replacements["material_list"]
427 ### Score files
428 for s in settings.get ("scores", []):
429 replacements["scoretype"] = s;
430 write_tex_file ("TeX_" + basename + "_Score_" + s + ".tex", templates["tex_score"] % replacements);
431 del replacements["scoretype"];
433 ### Orchestra material
434 replacements["scoretype"] = "InstrumentalParts"
435 lst = ""
436 for i in settings.get ("instruments", []):
437 replacements["instrument"] = i
438 lst += templates["itex_snippet_instrument"] % replacements;
439 del replacements["instrument"]
440 replacements["instrumentscores"] = lst;
441 write_tex_file ("TeX_" + basename + "_Score_Instruments.tex", templates["tex_instruments"] % replacements);
442 del replacements["instrumentscores"];
446 ######################################################################
447 # Creating Make files
448 ######################################################################
450 def generate_make_files (settings, templates, replacements):
451 movements = settings.get ("parts", {});
453 replacements["instruments"] = string.join (settings.get ("instruments", []));
454 replacements["scores"] = string.join (settings.get ("scores", []))## + " Instruments";
455 replacements["srcfiles"] = string.join (src_files);
457 write_file (output_dir + "Makefile", templates["Makefile"] % replacements);
459 del replacements["instruments"]
460 del replacements["scores"]
461 del replacements["srcfiles"]
462 return;
465 ######################################################################
466 # Link the orchestrallily package
467 ######################################################################
469 def generate_oly_link (settings, templates, replacements):
470 print ("Creating symlink %s to %s" % ("orchestrallily/", script_path))
471 try:
472 os.symlink ("../"+script_path, output_dir+"orchestrallily")
473 except OSError:
474 pass
476 ######################################################################
477 # Main function
478 ######################################################################
480 settings = load_settings (settings_file);
481 templates = load_templates (settings);
482 replacements = init_replacements (settings);
484 generate_movement_files (settings, templates, replacements);
485 generate_settings_files (settings, templates, replacements);
486 generate_instrument_files (settings, templates, replacements);
487 generate_score_files (settings, templates, replacements);
488 generate_tex_files (settings, templates, replacements);
489 generate_make_files (settings, templates, replacements);
490 generate_oly_link (settings, templates, replacements);