2 # -*- coding: utf-8 -*-
13 from jinja2
import Environment
, Template
, FileSystemLoader
15 program_name
= 'generate_oly_score';
16 settings_file
= 'oly_structure.def';
17 script_path
= os
.path
.dirname(__file__
);
19 ######################################################################
21 ######################################################################
23 help_text
= r
"""Usage: %(program_name)s [OPTIONS]... [DEF-FILE]
24 Create a complete file structure for a score using OrchestralLily.
25 If no definitions file it given, the file oly_structure.def is used
28 -h, --help print this help
29 -o, --output=DIR write output files to DIR (default: read from settings file)
33 sys
.stdout
.write (text
)
38 ######################################################################
40 ######################################################################
51 settings_file
= self
.load_options ();
52 self
.load (settings_file
);
53 self
.init_template_env ();
56 def output_dir (self
):
59 return self
.raw_data
.get ("defaults", {});
60 def score_names (self
):
61 return self
.raw_data
.get ("scores", ["Score"]);
63 def get_score_settings (self
, id):
64 settings
= self
.globals.copy();
65 settings
.update (self
.defaults ());
66 settings
.update ({"name": id})
67 settings
.update (self
.raw_data
.get (id, {}));
68 # TODO: Normalize part definitions
72 return "tex" in self
.raw_data
;
73 def get_tex_settings (self
):
74 settings
= self
.globals.copy();
75 settings
.update (self
.defaults ());
76 settings
.update (self
.raw_data
.get ("latex", {}));
79 def get_score_parts (self
, score_settings
):
80 scorename
= score_settings
.get ("name", "Score");
81 parts
= score_settings
.get ("parts", [scorename
]);
85 if isinstance (this_part
, basestring
):
86 this_part
= {"id": this_part
, "filename": this_part
}
87 elif not isinstance (this_part
, dict):
88 warning ("Invalid part list for score %a: %a" % (scorename
, p
));
90 if not this_part
["id"]:
91 this_part
["id"] = scorename
;
92 if not this_part
["filename"]:
93 this_part
["filename"] = this_part
["id"];
94 this_part
["score"] = scorename
;
95 this_part
.update (score_settings
);
96 result
.append (this_part
);
99 def load_options (self
):
100 (self
.options
, self
.arguments
) = getopt
.getopt (sys
.argv
[1:], 'ho:', ['help', 'output='])
101 for opt
in self
.options
:
104 if o
== '-h' or o
== '--help':
105 help (help_text
% globals ())
106 elif o
== '-o' or o
== '--output':
109 raise Exception ('unknown option: ' + o
)
111 return self
.arguments
[0]
113 return "oly_structure.def";
115 def load (self
, filename
):
117 in_f
= codecs
.open (filename
, "r", "utf-8")
120 self
.raw_data
= eval(s
);
122 print ("Unable to load settings file '%s'. Exiting..." % file_name
)
124 except SyntaxError as ex
:
126 print ("Unable to interpret settings file '%s', it's syntax is invalid. Exiting..." % file_name
);
129 self
.out_dir
= self
.raw_data
.get ("output_dir", self
.out_dir
) + "/";
131 def get_template_name (self
):
132 return self
.raw_data
.get ("template", "Full");
134 def init_template_env (self
):
137 templatename
= self
.get_template_name ();
138 path
= script_path
+ '/Templates/' + templatename
;
139 self
.template_env
= Environment (
140 loader
= FileSystemLoader(path
),
141 block_start_string
= '<$', block_end_string
= '$>',
142 variable_start_string
= '<<', variable_end_string
= '>>',
143 comment_start_string
= '<#', comment_end_string
= '#>',
144 #line_statement_prefix = '#'
146 def get_template (self
, template
):
147 return self
.template_env
.get_template (template
);
150 def init_arrays (self
):
152 self
.globals = self
.raw_data
.copy ();
153 del self
.globals["defaults"];
154 del self
.globals["latex"];
155 for i
in self
.globals.get ("scores", []):
158 def assemble_filename (self
, base
, id, name
, ext
):
159 return "_".join ([base
, id, name
]) + "." + ext
;
160 def assemble_movement_filename (self
, basename
, mvmnt
):
161 return self
.assemble_filename( basename
, "Music", mvmnt
, "ily");
162 def assemble_instrument_filename (self
, basename
, instr
):
163 return self
.assemble_filename( basename
, "Instrument", instr
, "ly");
164 def assemble_score_filename (self
, basename
, instr
):
165 return self
.assemble_filename( basename
, "Score", instr
, "ly");
166 def assemble_settings_filename (self
, basename
, s
):
167 return self
.assemble_filename( basename
, "Settings", s
, "ily");
170 ######################################################################
172 ######################################################################
174 def write_file (path
, fname
, contents
):
175 file_name
= path
+ fname
;
177 if os
.path
.exists (file_name
):
180 dir = os
.path
.dirname (fn
);
181 if not os
.path
.exists (dir):
185 out_f
= codecs
.open (fn
, "w", "utf-8")
186 s
= out_f
.write(contents
)
189 print ("Unable to write to output file '%s'. Exiting..." % fn
)
192 # If the file already existed, check if the new file is identical.
193 if (fn
!= file_name
):
194 patchfile
= os
.path
.join (dir, "patches", os
.path
.basename(file_name
) + ".patch")
195 if os
.path
.exists (patchfile
):
197 retcode
= subprocess
.call("patch -Ns \""+ fn
+ "\" \"" + patchfile
+ "\"", shell
=True)
199 print >>sys
.stderr
, "Unable to apply patch to file \"", fn
, "\"."
201 print >>sys
.stderr
, "Execution failed:", e
203 if filecmp
.cmp (fn
, file_name
):
206 print ("A file %s already existed, created new file %s." % (os
.path
.basename(file_name
), os
.path
.basename(fn
)))
210 ######################################################################
211 # Creating movement files
212 ######################################################################
215 def generate_movement_files (score_name
, score_settings
, settings
):
217 for part_settings
in settings
.get_score_parts (score_settings
):
218 template
= settings
.get_template ("Lily_Music_Movement.ily");
219 basename
= part_settings
.get ("basename", score_name
);
220 filename
= part_settings
.get ("filename", score_name
+ "Part");
221 filename
= settings
.assemble_movement_filename (basename
, filename
);
222 write_file (settings
.out_dir
, filename
, template
.render (part_settings
));
223 parts_files
.append (filename
);
229 ######################################################################
230 # Creating instrumental score files
231 ######################################################################
233 def generate_instrument_files (score_name
, score_settings
, settings
):
234 instrument_files
= [];
235 settings_files
= set ();
236 instrument_settings
= score_settings
.copy ();
237 instrument_settings
["parts"] = settings
.get_score_parts (score_settings
);
239 template
= settings
.get_template ("Lily_Instrument.ly");
240 basename
= score_settings
.get ("basename", score_name
);
242 for i
in score_settings
.get ("instruments", []):
243 instrument_settings
["instrument"] = i
;
244 if i
in score_settings
.get ("vocalvoices"):
245 instrument_settings
["settings"] = "VocalVoice";
247 instrument_settings
["settings"] = "Instrument";
248 settings_files
.add (instrument_settings
["settings"]);
249 filename
= settings
.assemble_instrument_filename (basename
, i
);
250 write_file (settings
.out_dir
, filename
, template
.render (instrument_settings
));
251 instrument_files
.append (filename
);
253 return (instrument_files
, settings_files
);
257 ######################################################################
258 # Creating score files
259 ######################################################################
263 "Particell": "Particell",
266 "OrganScore": "VocalScore",
267 "ChoralScore": "ChoralScore",
268 "LongScore": "FullScore",
269 "OriginalScore": "FullScore",
270 "Particell": "FullScore"
272 scores_cues
= ["ChoralScore", "VocalScore"];
274 def generate_score_files (score_name
, score_settings
, settings
):
276 settings_files
= set ();
277 s_settings
= score_settings
.copy ();
278 s_settings
["parts"] = settings
.get_score_parts (score_settings
);
279 s_settings
["fullscore"] = True;
281 template
= settings
.get_template ("Lily_Score.ly");
282 basename
= score_settings
.get ("basename", score_name
);
284 for s
in score_settings
.get ("scores", []):
285 fullsn
= score_name_map
.get (s
, (s
+ "Score"));
286 s_settings
["score"] = fullsn
;
287 s_settings
["nocues"] = fullsn
in scores_cues
;
288 s_settings
["settings"] = settings_map
.get (fullsn
,fullsn
)
289 settings_files
.add (s_settings
["settings"]);
290 filename
= settings
.assemble_score_filename (basename
, s
);
291 write_file (settings
.out_dir
, filename
, template
.render (s_settings
));
292 score_files
.append (filename
);
294 return (score_files
, settings_files
);
298 ######################################################################
299 # Creating settings files
300 ######################################################################
302 def write_settings_file (settings
, score_settings
, template
, filename
):
303 template
= settings
.get_template (template
);
304 basename
= score_settings
.get ("basename", "");
305 filename
= settings
.assemble_settings_filename (basename
, filename
);
306 write_file (settings
.out_dir
, filename
, template
.render (score_settings
));
309 def generate_settings_files (score_name
, score_settings
, settings
, settings_files
):
311 out_files
.append (write_settings_file (settings
, score_settings
, "Lily_Settings_Global.ily", "Global" ));
312 out_files
.append (write_settings_file (settings
, score_settings
, "Lily_Settings.ily", "" ));
314 for s
in settings_files
:
315 score_settings
["settings"] = s
.lower ();
316 out_files
.append (write_settings_file (settings
, score_settings
, "Lily_Settings_Generic.ily", s
));
322 ######################################################################
323 # Generation of Lilypond Files
324 ######################################################################
326 def generate_scores (settings
):
328 for s
in settings
.score_names ():
329 score_settings
= settings
.get_score_settings (s
);
330 parts_files
= generate_movement_files (s
, score_settings
, settings
);
331 (instrument_files
, isettings_files
) = generate_instrument_files (s
, score_settings
, settings
);
332 (score_files
, ssettings_files
) = generate_score_files (s
, score_settings
, settings
);
333 included_settings_files
= ssettings_files | isettings_files
;
334 score_settings
["parts_files"] = parts_files
;
335 settings_files
= generate_settings_files (s
, score_settings
, settings
, included_settings_files
);
336 files
["s"] = {"settings": settings_files
,
337 "scores": score_files
,
338 "instruments": instrument_files
,
339 "parts": parts_files
};
344 ######################################################################
345 # Creating LaTeX files
346 ######################################################################
348 def write_tex_file (score_settings
, template
, filename
):
349 #def write_tex_file (settings, template,
350 #score_settings, template, filename):
351 template
= settings
.get_template (template
);
352 basename
= score_settings
.get ("basename", "");
353 filename
= settings
.assemble_settings_filename (basename
, filename
);
354 write_file (settings
.out_dir
, filename
, template
.render (score_settings
));
357 #def write_tex_file (fn, contents):
358 # write_file (output_dir + "/"+ fn, contents );
359 # tex_files.append (fn)
362 # "Vocal": ["vocalscore"],
365 def generate_tex_files (settings
, lily_files
):
368 tex_settings
= settings
.get_tex_settings ();
369 tex_settings
["lily_files"] = lily_files
;
371 files
.append (write_tex_file ("TeX_Include_About.itex", "Include_About"));
373 for s
in settings
.score_names ():
375 score_settings
= settings
.get_score_settings (s
);
376 parts_files
= generate_movement_files (s
, score_settings
, settings
);
377 (instrument_files
, isettings_files
) = generate_instrument_files (s
, score_settings
, settings
);
378 (score_files
, ssettings_files
) = generate_score_files (s
, score_settings
, settings
);
379 included_settings_files
= ssettings_files | isettings_files
;
380 score_settings
["parts_files"] = parts_files
;
381 settings_files
= generate_settings_files (s
, score_settings
, settings
, included_settings_files
);
382 files
["s"] = {"settings": settings_files
,
383 "scores": score_files
,
384 "instruments": instrument_files
,
385 "parts": parts_files
};
388 #def generate_tex_files (settings, templates, replacements):
389 # basename = replacements["tex_basename"]
390 # write_tex_file ("TeX_" + basename + "_Settings.itex", templates["itex_settings"] % replacements);
391 # write_tex_file ("TeX_" + basename + "_Include_Coverpage.itex", templates["itex_coverpage"] % replacements);
392 # write_tex_file ("TeX_" + basename + "_Include_Backpage.itex", templates["itex_backpage"] % replacements);
393 # write_tex_file ("TeX_" + basename + "_Include_About.itex", templates["itex_about"] % replacements);
394 # write_tex_file ("TeX_" + basename + "_Include_Leben.itex", templates["itex_leben"] % replacements);
395 # write_tex_file ("TeX_" + basename + "_Include_Todo.itex", templates["itex_todo"] % replacements);
396 # write_tex_file ("TeX_" + basename + "_Include_Preface_Long.itex", templates["itex_preface_long"] % replacements);
397 # write_tex_file ("TeX_" + basename + "_Include_Preface_Short.itex", templates["itex_preface_short"] % replacements);
401 # for m in settings.get ("parts",[]):
402 # if type(m) == dict:
403 # replacements["movement"] = m.get ("name", "TODO")
404 # replacements["movementname"] = m.get ("piece", replacements["movement"])
406 # replacements["movement"] = m
407 # replacements["movementname"] = m
408 # txt += (templates["itex_snippet_textmovement"] % replacements);
409 # kb += (templates["itex_snippet_kritbericht"] % replacements);
410 # del replacements["movement"]
411 # del replacements["movementname"]
412 # replacements["movementtexts"] = txt
413 # replacements["kritbericht_movements"] = kb
414 # write_tex_file ("TeX_" + basename + "_Include_Text.itex", templates["itex_text"] % replacements);
415 # write_tex_file ("TeX_" + basename + "_Include_KritBericht.itex", templates["itex_kritbericht"] % replacements);
416 # del replacements["movementtexts"]
417 # del replacements["kritbericht_movements"]
419 # ### Contents and Material:
421 # for i in settings.get ("scores", []):
422 # replacements["type"] = i + "Score";
423 # lst += templates["itex_snippet_material"] % replacements
424 # replacements["type"] = "Instruments"
425 # lst += templates["itex_snippet_material"] % replacements
426 # for i in settings.get ("instruments", []):
427 # replacements["type"] = i;
428 # lst += templates["itex_snippet_material"] % replacements
429 # del replacements["type"]
430 # replacements["material_list"] = lst;
431 # write_tex_file ("TeX_" + basename + "_Include_ContentsMaterial.itex", templates["itex_contents"] % replacements);
432 # del replacements["material_list"]
435 # for s in settings.get ("scores", []):
436 # opts = ",".join (packageoptions.get (s, []));
438 # opts = "["+opts+"]";
439 # replacements["ekopts"] = opts;
440 # replacements["scoretype"] = s;
441 # write_tex_file ("TeX_" + basename + "_Score_" + s + ".tex", templates["tex_score"] % replacements);
442 # del replacements["scoretype"];
444 # ### Orchestra material
445 # replacements["scoretype"] = "InstrumentalParts"
447 # for i in settings.get ("instruments", []):
448 # opts = ",".join (packageoptions.get (s, []));
450 # opts = "["+opts+"]";
451 # replacements["ekopts"] = opts;
452 # replacements["instrument"] = i
453 # lst += templates["itex_snippet_instrument"] % replacements;
454 # del replacements["instrument"]
455 # replacements["instrumentscores"] = lst;
456 # write_tex_file ("TeX_" + basename + "_Score_Instruments.tex", templates["tex_instruments"] % replacements);
457 # del replacements["instrumentscores"];
458 # del replacements["ekopts"];
462 ######################################################################
463 # Creating Make files
464 ######################################################################
466 #def generate_make_files (settings, templates, replacements):
467 # movements = settings.get ("parts", {});
469 # replacements["instruments"] = string.join (settings.get ("instruments", []));
470 # replacements["scores"] = string.join (settings.get ("scores", []))## + " Instruments";
471 # replacements["srcfiles"] = string.join (src_files);
473 # write_file (output_dir + "Makefile", templates["Makefile"] % replacements);
475 # del replacements["instruments"]
476 # del replacements["scores"]
477 # del replacements["srcfiles"]
481 ######################################################################
482 # Link the orchestrallily package
483 ######################################################################
485 def generate_oly_link (settings
):
488 os
.symlink ("../"+script_path
, settings
.out_dir
+"orchestrallily")
492 ######################################################################
494 ######################################################################
497 settings
= Settings ();
498 print ("Creating OrchestralLily template in \"%s\", using template \"%s\"." %
499 (settings
.out_dir
, settings
.get_template_name () ));
501 print ("Creating Lilypond files")
502 lily_files
= generate_scores (settings
);
504 if settings
.has_tex ():
505 print ("Creating LaTeX files")
506 tex_files
= generate_tex_files (settings
, lily_files
);
507 print ("Creating OrchestralLily package link")
508 generate_oly_link (settings
);
509 print ("Creating Makefile")
510 # generate_make_files (settings, templates, replacements);