Use cues in Choral and Vocal scores
[orchestrallily.git] / generate_oly_score.py
blob1878a95f7f19b05df55d207fe52b621435b70bb7
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__)
18 settings_files = []
19 src_files = []
20 score_files = []
21 tex_files = []
23 ######################################################################
24 # Options handling
25 ######################################################################
27 help_text = r"""Usage: %(program_name)s [OPTIONS]... [DEF-FILE]
28 Create a complete file structure for a score using OrchestralLily.
29 If no definitions file it given, the file oly_structure.def is used
31 Options:
32 -h, --help print this help
33 -o, --output=NAME write tely doc to NAME (default: read from settings or current directory)
34 """
36 def help (text):
37 sys.stdout.write ( text)
38 sys.exit (0)
40 (options, files) = getopt.getopt (sys.argv[1:], 'ho:',
41 ['help', 'output='])
43 for opt in options:
44 o = opt[0]
45 a = opt[1]
46 if o == '-h' or o == '--help':
47 help (help_text % vars ())
48 #elif o == '-s' or o == '--settings':
49 #settings_file = a
50 elif o == '-o' or o == '--output':
51 output_dir = a
52 else:
53 raise Exception ('unknown option: ' + o)
55 if files:
56 settings_file = files[0]
59 ######################################################################
60 # Settings loading
61 ######################################################################
63 def load_settings (file_name):
64 global output_dir;
65 try:
66 in_f = open (file_name)
67 s = in_f.read()
68 in_f.close()
69 settings_expr = eval(s);
70 except IOError:
71 print ("Unable to load settings file '%s'. Exiting..." % file_name)
72 exit (-1);
73 except SyntaxError as ex:
74 print ex;
75 print ("Unable to interpret settings file '%s', it's syntax is invalid. Exiting..." % file_name);
76 exit (-1);
78 if not output_dir:
79 output_dir = settings_expr.get ("output_dir", output_dir) + "/";
80 return settings_expr;
83 ######################################################################
84 # File Writing
85 ######################################################################
87 def write_file (file_name, contents):
88 fn = file_name
89 if os.path.exists (file_name):
90 fn += ".new"
92 dir = os.path.dirname (fn);
93 if not os.path.exists (dir):
94 os.mkdir (dir)
96 try:
97 out_f = open (fn, "w")
98 s = out_f.write(contents)
99 out_f.close()
100 except IOError:
101 print ("Unable to write to output file '%s'. Exiting..." % fn)
102 exit (-1);
104 # If the file already existed, check if the new file is identical.
105 if (fn != file_name):
106 patchfile = os.path.join (dir, "patches", os.path.basename(file_name) + ".patch")
107 if os.path.exists (patchfile):
108 #print ("Trying to apply patch %s" % patchfile)
109 try:
110 #retcode = subprocess.call(["patch ", fn, patchfile], shell=True)
111 retcode = subprocess.call("patch -Ns \""+ fn+ "\" \"" + patchfile + "\"", shell=True)
112 if retcode < 0:
113 print >>sys.stderr, "Unable to apply patch to file \"", fn, "\"."
114 #else:
115 #print >>sys.stderr, "Child returned", retcode
116 except OSError, e:
117 print >>sys.stderr, "Execution failed:", e
118 #else:
119 #print ("Unable to find patch %s" % patchfile)
121 if filecmp.cmp (fn, file_name):
122 os.unlink (fn);
123 else:
124 print ("A file %s already existed, created new file %s." % (os.path.basename(file_name), os.path.basename(fn)))
128 ######################################################################
129 # Templates
130 ######################################################################
132 def init_replacements (settings):
133 rep = settings.copy ();
134 if not rep["basename"]:
135 rep["basename"] = "Score";
136 if not rep["tex_basename"]:
137 rep["tex_basename"] = rep["basename"];
138 del rep["parts"];
139 del rep["instruments"];
140 del rep["lyrics"];
141 del rep["scores"];
143 rep["cues"] = "";
145 return rep
148 ######################################################################
149 # Templates
150 ######################################################################
152 template_names = {
153 "Makefile": "Makefile",
155 "movement": "TEMPLATE_Include_Movement.ily",
156 "snippet_def_lyrics": "TEMPLATE_Snippet_DefinitionLyrics.ily",
157 "snippet_def_music": "TEMPLATE_Snippet_DefinitionMusic.ily",
158 "snippet_def_include": "TEMPLATE_Snippet_DefinitionInclude.ily",
160 "settings": "TEMPLATE_Settings.ily",
161 "settings_global": "TEMPLATE_Settings_Global.ily",
162 "settings_fullscore": "TEMPLATE_Settings_FullScore.ily",
163 "settings_instrument": "TEMPLATE_Settings_Instrument.ily",
164 "settings_vocalvoice": "TEMPLATE_Settings_VocalVoice.ily",
165 "settings_vocalscore": "TEMPLATE_Settings_VocalScore.ily",
167 "snippet_part_score": "TEMPLATE_Snippet_PartScore.ily",
169 "score_full": "TEMPLATE_Score_FullScore.ly",
170 "score_instrument": "TEMPLATE_Score_Instrument.ly",
172 "itex_settings": "TEXTEMPLATE_Settings.itex",
173 "itex_about": "TEXTEMPLATE_Include_About.itex",
174 "itex_contents": "TEXTEMPLATE_Include_ContentsMaterial.itex",
175 "itex_leben": "TEXTEMPLATE_Include_Leben.itex",
176 "itex_text": "TEXTEMPLATE_Include_Text.itex",
178 "itex_todo": "TEXTEMPLATE_Include_Todo.itex",
179 "itex_preface_long": "TEXTEMPLATE_Include_Preface_Long.itex",
180 "itex_preface_short": "TEXTEMPLATE_Include_Preface_Short.itex",
182 "itex_coverpage": "TEXTEMPLATE_Include_Coverpage.itex",
183 "itex_backpage": "TEXTEMPLATE_Include_Backpage.itex",
185 "itex_kritbericht": "TEXTEMPLATE_Include_KritBericht.itex",
186 "tex_score": "TEXTEMPLATE_Score.tex",
187 "tex_instruments": "TEXTEMPLATE_Score_Instruments.tex",
189 "itex_snippet_textmovement": "TEXTEMPLATE_Snippet_TextMovement.itex",
190 "itex_snippet_kritbericht": "TEXTEMPLATE_Snippet_KritBericht_Movement.itex",
191 "itex_snippet_material": "TEXTEMPLATE_Snippet_Material_Score.itex",
192 "itex_snippet_instrument": "TEXTEMPLATE_Snippet_Instrument.itex",
195 def load_file (file_name):
196 s = False;
197 try:
198 in_f = open (file_name, "rb")
199 s = in_f.read()
200 in_f.close()
201 except IOError:
202 print ("Unable to load file %s" % file_name);
203 return s
205 def load_templates (settings):
206 templates = {};
207 template_path = script_path + "/Templates/";
209 for id in template_names:
210 filename = template_names[id]
211 s = load_file (template_path + filename);
212 if s:
213 templates[id] = s
214 else:
215 print ("Unable to load template file %s (id %s).\n" % (filename, id));
216 return templates;
219 ######################################################################
220 # Creating settings files
221 ######################################################################
223 def write_settings_file (fn, contents):
224 write_file (fn, contents );
225 settings_files.append (fn)
227 def generate_settings_files (settings, templates, replacements):
228 inc = "";
229 for m in src_files:
230 replacements["filename"] = m
231 inc += (templates["snippet_def_include"] % replacements)
232 del replacements["filename"];
233 replacements["includefiles"] = inc
235 basename = replacements["basename"];
236 write_settings_file (output_dir + basename + "_Settings.ily", templates["settings"] % replacements );
237 write_file (output_dir + basename + "_Settings_Global.ily", templates["settings_global"] % replacements );
239 if (settings.get("scores", [])):
240 write_settings_file (output_dir + basename + "_Settings_FullScore.ily", templates["settings_fullscore"] % replacements );
241 if (settings.get("instruments", [])):
242 write_settings_file (output_dir + basename + "_Settings_Instrument.ily", templates["settings_instrument"] % replacements );
243 else:
244 print ("WARNING: No instruments: %s" % settings.get("instruments", []));
245 if (settings.get("lyrics", [])):
246 write_settings_file (output_dir + basename + "_Settings_VocalVoice.ily", templates["settings_vocalvoice"] % replacements );
247 write_settings_file (output_dir + basename + "_Settings_VocalScore.ily", templates["settings_vocalscore"] % replacements );
248 del replacements["includefiles"]
251 ######################################################################
252 # Creating movement files
253 ######################################################################
255 def generate_movement_files (settings, templates, replacements):
256 movements = settings.get ("parts", []);
257 instruments = settings.get ("instruments", []);
258 lyrics = settings.get ("lyrics", []);
259 if (movements == {}):
260 print ("No parts defined in settings file...");
261 exit (-1);
262 if (instruments == []):
263 print ("No instruments defined in settings file...");
264 exit (-1);
266 for m in movements:
267 filename_ext = ""
268 if type (m) == dict:
269 name = m.get ("name", "TODO")
270 filename_ext = m.get ("filename", name)
271 replacements["movement"] = name
272 replacements["movementname"] = m.get ("piece", name)
273 replacements["movementtacet"] = m.get ("piecetacet", replacements["movementname"] + " tacet")
274 elif type(m) == str:
275 filename_ext = m
276 replacements["movement"] = m
277 replacements["movementname"] = m
278 replacements["movementtacet"] = (m + " tacet")
279 else:
280 print ("Type of movement entry %s is not a dict or a string" % m)
281 continue
283 defs = ""
284 for i in instruments:
285 replacements["instrument"] = i
286 defs += (templates["snippet_def_music"] % replacements);
287 if (i in lyrics):
288 defs += (templates["snippet_def_lyrics"] % replacements);
289 del replacements["instrument"]
290 replacements["movementinstrumentdefinitions"] = defs
291 fn = replacements["basename"] + "_Music_" + filename_ext + ".ily"
292 write_file (output_dir + fn, templates["movement"] % replacements);
293 src_files.append (fn)
294 del replacements["movement"]
295 del replacements["movementname"]
296 del replacements["movementtacet"]
299 ######################################################################
300 # Creating instrumental score files
301 ######################################################################
303 def generate_instrument_files (settings, templates, replacements):
304 movements = settings.get ("parts", []);
305 instruments = settings.get ("instruments", []);
306 lyrics = settings.get ("lyrics", []);
308 for i in instruments:
309 replacements["instrument"] = i
310 defs = ""
311 for m in movements:
312 if type(m) == dict:
313 replacements["movement"] = m.get ("name", "TODO")
314 replacements["movementname"] = m.get ("piece", replacements["movement"])
315 else:
316 replacements["movement"] = m
317 replacements["movementname"] = m
318 defs += (templates["snippet_part_score"] % replacements);
319 del replacements["movement"]
320 del replacements["movementname"]
321 replacements["partlist"] = defs
322 if i in lyrics:
323 settings = "VocalVoice"
324 else:
325 settings = "Instrument"
326 replacements["settings"] = settings
327 fn = output_dir + replacements["basename"] + "_Instrument_" + i + ".ly"
328 write_file (fn, templates["score_instrument"] % replacements);
329 score_files.append (fn)
330 del replacements["instrument"]
331 del replacements["settings"]
333 ######################################################################
334 # Creating score files
335 ######################################################################
338 score_names = {
339 "ParticellScore": "Particell",
341 score_settings = {
342 "OrganScore": "VocalScore",
343 "ChoralScore": "VocalScore",
344 "LongScore": "FullScore",
345 "OriginalScore": "FullScore",
346 "Particell": "FullScore"
348 scores_cues = ["ChoralScore", "VocalScore"];
350 def generate_score_files (settings, templates, replacements):
351 movements = settings.get ("parts",[]);
352 scores = settings.get ("scores", []);
354 for i in scores:
355 sn = (i + "Score")
356 fullsn = score_names.get (sn, sn);
357 if fullsn in scores_cues:
358 replacements["cues"] = "";
359 else:
360 replacements["cues"] = "NoCues";
361 replacements["score"] = fullsn
362 replacements["settings"] = score_settings.get (fullsn,fullsn)
363 replacements["instrument"] = replacements["score"]
364 defs = ""
365 for m in movements:
366 if type(m) == dict:
367 replacements["movement"] = m.get ("name", "TODO")
368 replacements["movementname"] = m.get ("piece", replacements["movement"])
369 else:
370 replacements["movement"] = m
371 replacements["movementname"] = m
372 defs += (templates["snippet_part_score"] % replacements);
373 del replacements["movement"]
374 del replacements["movementname"]
375 replacements["partlist"] = defs
376 filename = output_dir + replacements["basename"] + "_Score_" + i + ".ly";
377 if i == "":
378 filename = output_dir + replacements["basename"] + "_Score.ly"
379 write_file (filename, templates["score_full"] % replacements);
380 score_files.append (filename)
381 del replacements["score"]
382 del replacements["instrument"]
384 replacements["cues"] = "";
387 ######################################################################
388 # Creating LaTeX files
389 ######################################################################
391 def write_tex_file (fn, contents):
392 write_file (output_dir + "/"+ fn, contents );
393 tex_files.append (fn)
395 def generate_tex_files (settings, templates, replacements):
396 basename = replacements["tex_basename"]
397 write_tex_file ("TeX_" + basename + "_Settings.itex", templates["itex_settings"] % replacements);
398 write_tex_file ("TeX_" + basename + "_Include_Coverpage.itex", templates["itex_coverpage"] % replacements);
399 write_tex_file ("TeX_" + basename + "_Include_Backpage.itex", templates["itex_backpage"] % replacements);
400 write_tex_file ("TeX_" + basename + "_Include_About.itex", templates["itex_about"] % replacements);
401 write_tex_file ("TeX_" + basename + "_Include_Leben.itex", templates["itex_leben"] % replacements);
402 write_tex_file ("TeX_" + basename + "_Include_Todo.itex", templates["itex_todo"] % replacements);
403 write_tex_file ("TeX_" + basename + "_Include_Preface_Long.itex", templates["itex_preface_long"] % replacements);
404 write_tex_file ("TeX_" + basename + "_Include_Preface_Short.itex", templates["itex_preface_short"] % replacements);
406 txt = ""
407 kb = ""
408 for m in settings.get ("parts",[]):
409 if type(m) == dict:
410 replacements["movement"] = m.get ("name", "TODO")
411 replacements["movementname"] = m.get ("piece", replacements["movement"])
412 else:
413 replacements["movement"] = m
414 replacements["movementname"] = m
415 txt += (templates["itex_snippet_textmovement"] % replacements);
416 kb += (templates["itex_snippet_kritbericht"] % replacements);
417 del replacements["movement"]
418 del replacements["movementname"]
419 replacements["movementtexts"] = txt
420 replacements["kritbericht_movements"] = kb
421 write_tex_file ("TeX_" + basename + "_Include_Text.itex", templates["itex_text"] % replacements);
422 write_tex_file ("TeX_" + basename + "_Include_KritBericht.itex", templates["itex_kritbericht"] % replacements);
423 del replacements["movementtexts"]
424 del replacements["kritbericht_movements"]
426 ### Contents and Material:
427 lst = ""
428 for i in settings.get ("scores", []):
429 replacements["type"] = i + "Score";
430 lst += templates["itex_snippet_material"] % replacements
431 replacements["type"] = "Instruments"
432 lst += templates["itex_snippet_material"] % replacements
433 for i in settings.get ("instruments", []):
434 replacements["type"] = i;
435 lst += templates["itex_snippet_material"] % replacements
436 del replacements["type"]
437 replacements["material_list"] = lst;
438 write_tex_file ("TeX_" + basename + "_Include_ContentsMaterial.itex", templates["itex_contents"] % replacements);
439 del replacements["material_list"]
441 ### Score files
442 for s in settings.get ("scores", []):
443 replacements["scoretype"] = s;
444 write_tex_file ("TeX_" + basename + "_Score_" + s + ".tex", templates["tex_score"] % replacements);
445 del replacements["scoretype"];
447 ### Orchestra material
448 replacements["scoretype"] = "InstrumentalParts"
449 lst = ""
450 for i in settings.get ("instruments", []):
451 replacements["instrument"] = i
452 lst += templates["itex_snippet_instrument"] % replacements;
453 del replacements["instrument"]
454 replacements["instrumentscores"] = lst;
455 write_tex_file ("TeX_" + basename + "_Score_Instruments.tex", templates["tex_instruments"] % replacements);
456 del replacements["instrumentscores"];
460 ######################################################################
461 # Creating Make files
462 ######################################################################
464 def generate_make_files (settings, templates, replacements):
465 movements = settings.get ("parts", {});
467 replacements["instruments"] = string.join (settings.get ("instruments", []));
468 replacements["scores"] = string.join (settings.get ("scores", []))## + " Instruments";
469 replacements["srcfiles"] = string.join (src_files);
471 write_file (output_dir + "Makefile", templates["Makefile"] % replacements);
473 del replacements["instruments"]
474 del replacements["scores"]
475 del replacements["srcfiles"]
476 return;
479 ######################################################################
480 # Link the orchestrallily package
481 ######################################################################
483 def generate_oly_link (settings, templates, replacements):
484 print ("Creating symlink %s to %s" % ("orchestrallily/", script_path))
485 try:
486 os.symlink ("../"+script_path, output_dir+"orchestrallily")
487 except OSError:
488 pass
490 ######################################################################
491 # Main function
492 ######################################################################
494 settings = load_settings (settings_file);
495 templates = load_templates (settings);
496 replacements = init_replacements (settings);
498 generate_movement_files (settings, templates, replacements);
499 generate_settings_files (settings, templates, replacements);
500 generate_instrument_files (settings, templates, replacements);
501 generate_score_files (settings, templates, replacements);
502 generate_tex_files (settings, templates, replacements);
503 generate_make_files (settings, templates, replacements);
504 generate_oly_link (settings, templates, replacements);