From: Reinhold Kainhofer Date: Fri, 11 Jun 2010 19:19:19 +0000 (+0200) Subject: Lilypond-book: Implement MusicXML support in lilypond-book X-Git-Tag: release/2.15.9-1~9^2~44 X-Git-Url: https://git.donarmstrong.com/?a=commitdiff_plain;h=81b5ad4f11cdb296c69fcd2259effbc75a3c9054;p=lilypond.git Lilypond-book: Implement MusicXML support in lilypond-book This patch adds support for including MusicXML files into documents processed by lilypond-book. In particular: -) HTML: filename.xml -) TeX: \musicxmlfile[options]{filename.xml} -) Texinfo: @musicxmlfile[options]{filename.xml} Since MusicXML is so verbose, it doesn't make much sense to support inline MusicXML. The snippets are basically processed like a lilypond file, except that musicxml2ly is run on them (with the options given for the snippet) and the returned lilypond code is then processed as if it were the original contents of the file. For the output links, however, the html and texinfo pages will link to the .xml/.mxl file rather than the intermediate .ly file. If a file has the extension .mxl, it is assumed to be a compressed MusicXML file (alternatively, the 'compressed' snippet option can be given). What's missing is proper documentation in "Usage". I'm unsure how to document such new snippet types... --- diff --git a/input/regression/lilypond-book/html-musicxml-file-compressed.htmly b/input/regression/lilypond-book/html-musicxml-file-compressed.htmly new file mode 100644 index 0000000000..a839edd60e --- /dev/null +++ b/input/regression/lilypond-book/html-musicxml-file-compressed.htmly @@ -0,0 +1,6 @@ + + +Including a compressed MusicXML file: +include.mxl + + diff --git a/input/regression/lilypond-book/html-musicxml-file-options.htmly b/input/regression/lilypond-book/html-musicxml-file-options.htmly new file mode 100644 index 0000000000..6e05bbe3c8 --- /dev/null +++ b/input/regression/lilypond-book/html-musicxml-file-options.htmly @@ -0,0 +1,6 @@ + + +Including a MusicXML file: +include.xml + + diff --git a/input/regression/lilypond-book/html-musicxml-file.htmly b/input/regression/lilypond-book/html-musicxml-file.htmly new file mode 100644 index 0000000000..6f8d7e3657 --- /dev/null +++ b/input/regression/lilypond-book/html-musicxml-file.htmly @@ -0,0 +1,6 @@ + + +Including a MusicXML file: +include.xml + + diff --git a/input/regression/lilypond-book/include.mxl b/input/regression/lilypond-book/include.mxl new file mode 100644 index 0000000000..d709094487 Binary files /dev/null and b/input/regression/lilypond-book/include.mxl differ diff --git a/input/regression/lilypond-book/include.xml b/input/regression/lilypond-book/include.xml new file mode 100644 index 0000000000..839e14599e --- /dev/null +++ b/input/regression/lilypond-book/include.xml @@ -0,0 +1,55 @@ + + + + + + One simple chord + consisting of two notes. + + + + + MusicXML Part + + + + + + 960 + + + G + 2 + + + + + B + 4 + + 960 + 1 + quarter + + + + + G + 4 + + 960 + 1 + quarter + + + + 960 + 1 + quarter + + + + diff --git a/input/regression/lilypond-book/tex-musicxml-file-options.lytex b/input/regression/lilypond-book/tex-musicxml-file-options.lytex new file mode 100644 index 0000000000..154ed6646a --- /dev/null +++ b/input/regression/lilypond-book/tex-musicxml-file-options.lytex @@ -0,0 +1,5 @@ +\documentclass{article} +\begin{document} +Including MusicMXL file: +\musicxmlfile[language=deutsch,absolute,no-beaming]{include.xml} +\end{document} diff --git a/input/regression/lilypond-book/tex-musicxml-file.lytex b/input/regression/lilypond-book/tex-musicxml-file.lytex new file mode 100644 index 0000000000..2995b35cb3 --- /dev/null +++ b/input/regression/lilypond-book/tex-musicxml-file.lytex @@ -0,0 +1,5 @@ +\documentclass{article} +\begin{document} +Including MusicMXL file: +\musicxmlfile{include.xml} +\end{document} diff --git a/input/regression/lilypond-book/texinfo-musicxml-file-options.tely b/input/regression/lilypond-book/texinfo-musicxml-file-options.tely new file mode 100644 index 0000000000..85454531fb --- /dev/null +++ b/input/regression/lilypond-book/texinfo-musicxml-file-options.tely @@ -0,0 +1,12 @@ +\input texinfo @c -*- coding: utf-8; mode: texinfo; -*- +@setfilename texinfo-musicxml-file.info +@settitle MusicXML inside texinfo + +@node Top +@top MusicMXL in texinfo + +MusicXML included in texinfo +@musicxmlfile[language=deutsch,absolute,no-beaming]{include.xml} + + +@bye diff --git a/input/regression/lilypond-book/texinfo-musicxml-file.tely b/input/regression/lilypond-book/texinfo-musicxml-file.tely new file mode 100644 index 0000000000..8ff23841f2 --- /dev/null +++ b/input/regression/lilypond-book/texinfo-musicxml-file.tely @@ -0,0 +1,12 @@ +\input texinfo @c -*- coding: utf-8; mode: texinfo; -*- +@setfilename texinfo-musicxml-file.info +@settitle MusicXML inside texinfo + +@node Top +@top MusicMXL in texinfo + +MusicXML included in texinfo +@musicxmlfile{include.xml} + + +@bye diff --git a/python/book_base.py b/python/book_base.py index b73f37336e..b43b9e00d4 100644 --- a/python/book_base.py +++ b/python/book_base.py @@ -172,7 +172,8 @@ class BookOutputFormat: rep = snippet.get_replacements () if PRINTFILENAME in snippet.option_dict: rep['base'] = basename - rep['filename'] = os.path.basename (snippet.substring ('filename')) + rep['filename'] = os.path.basename (snippet.filename) + rep['ext'] = snippet.ext str = self.output[PRINTFILENAME] % rep return str diff --git a/python/book_docbook.py b/python/book_docbook.py index 5ae766bbaf..ef009935d6 100644 --- a/python/book_docbook.py +++ b/python/book_docbook.py @@ -79,7 +79,7 @@ Docbook_output = { PRINTFILENAME: r''' - + %(filename)s diff --git a/python/book_html.py b/python/book_html.py index cf56dd5f49..ac70b743a0 100644 --- a/python/book_html.py +++ b/python/book_html.py @@ -40,6 +40,13 @@ HTML_snippet_res = { 'multiline_comment': r'''(?smx)(?P\s*(?!@c\s+)(?P)\s)''', + 'musicxml_file': + r'''(?mx) + (?P + .*?)\s*> + \s*(?P.*?)\s* + )''', + 'verb': r'''(?x)(?P(?P
.*?
))''', @@ -62,7 +69,7 @@ HTML_output = {

''', BEFORE: r'''

- ''', + ''', OUTPUT: r''' %(alt)s''', - PRINTFILENAME: '

%(filename)s

', + PRINTFILENAME: '

%(filename)s

', QUOTE: r'''
%(str)s @@ -118,6 +125,8 @@ class BookHTMLOutputFormat (BookBase.BookOutputFormat): str = '' rep = snippet.get_replacements (); rep['base'] = basename + rep['filename'] = os.path.basename (snippet.filename) + rep['ext'] = snippet.ext str += self.output_print_filename (basename, snippet) if VERBATIM in snippet.option_dict: rep['verb'] = BookBase.verbatim_html (snippet.verb_ly ()) diff --git a/python/book_latex.py b/python/book_latex.py index 677e88a1f7..8097443f7f 100644 --- a/python/book_latex.py +++ b/python/book_latex.py @@ -68,6 +68,17 @@ Latex_snippet_res = { (?P\S+?) })''', + 'musicxml_file': + r'''(?smx) + ^[^%\n]*? + (?P + \\musicxmlfile\s*( + \[ + \s*(?P.*?)\s* + \])?\s*\{ + (?P\S+?) + })''', + 'singleline_comment': r'''(?mx) ^.*? diff --git a/python/book_snippets.py b/python/book_snippets.py index b7c3ddf0d6..aedee3ceef 100644 --- a/python/book_snippets.py +++ b/python/book_snippets.py @@ -659,6 +659,11 @@ printing diff against existing file." % filename) os.makedirs (dst_path) os.link (src, dst) + def additional_files_to_consider (self, base, full): + return [] + def additional_files_required (self, base, full): + return [] + def all_output_files (self, output_dir, output_dir_files): """Return all files generated in lily_output_dir, a set. @@ -681,8 +686,7 @@ printing diff against existing file." % filename) # UGH - junk self.global_options skip_lily = self.global_options.skip_lilypond_run - for required in [base + '.ly', - base + '.txt']: + for required in [base + '.ly']: require_file (required) if not skip_lily: require_file (base + '-systems.count') @@ -722,6 +726,8 @@ printing diff against existing file." % filename) if 'ddump-signature' in self.global_options.process_cmd: consider_file (systemfile + '.signature') + map (consider_file, self.additional_files_to_consider (base, full)) + map (require_file, self.additional_files_required (base, full)) return (result, missing) @@ -808,7 +814,9 @@ re_end_verbatim = re.compile (r'\s+%.*?end verbatim.*$', re.M) class LilypondFileSnippet (LilypondSnippet): def __init__ (self, type, match, formatter, line_number, global_options): LilypondSnippet.__init__ (self, type, match, formatter, line_number, global_options) - self.contents = file (BookBase.find_file (self.substring ('filename'), global_options.include_path)).read () + self.filename = self.substring ('filename') + self.ext = os.path.splitext (os.path.basename (self.filename))[1] + self.contents = file (BookBase.find_file (self.filename, global_options.include_path)).read () def get_snippet_code (self): return self.contents; @@ -824,18 +832,110 @@ class LilypondFileSnippet (LilypondSnippet): return s def ly (self): - name = self.substring ('filename') + name = self.filename return ('\\sourcefilename \"%s\"\n\\sourcefileline 0\n%s' % (name, self.contents)) def final_basename (self): if self.global_options.use_source_file_names: - base = os.path.splitext (os.path.basename (self.substring ('filename')))[0] + base = os.path.splitext (os.path.basename (self.filename))[0] return base else: return self.basename () +class MusicXMLFileSnippet (LilypondFileSnippet): + def __init__ (self, type, match, formatter, line_number, global_options): + LilypondFileSnippet.__init__ (self, type, match, formatter, line_number, global_options) + self.compressed = False + self.converted_ly = None + self.musicxml_options_dict = { + 'verbose': '--verbose', + 'lxml': '--lxml', + 'compressed': '--compressed', + 'relative': '--relative', + 'absolute': '--absolute', + 'no-articulation-directions': '--no-articulation-directions', + 'no-rest-positions': '--no-rest-positions', + 'no-page-layout': '--no-page-layout', + 'no-beaming': '--no-beaming', + 'language': '--language', + } + + def snippet_options (self): + return self.musicxml_options_dict.keys () + + def convert_from_musicxml (self): + name = self.filename + option_list = [] + for (key, value) in self.option_dict.items (): + cmd_key = self.musicxml_options_dict.get (key, None) + if cmd_key == None: + continue + if value == None: + option_list.append (cmd_key) + else: + option_list.append (cmd_key + '=' + value) + if ('.mxl' in name) and ('--compressed' not in option_list): + option_list.append ('--compressed') + self.compressed = True + opts = " ".join (option_list) + + ly_code = self.filter_pipe (self.contents, 'musicxml2ly %s --out=- - ' % opts) + return ly_code + + def ly (self): + if self.converted_ly == None: + self.converted_ly = self.convert_from_musicxml () + name = self.filename + return ('\\sourcefilename \"%s\"\n\\sourcefileline 0\n%s' + % (name, self.converted_ly)) + + def additional_files_required (self, base, full): + result = []; + if self.compressed: + result.append (base + '.mxl') + else: + result.append (base + '.xml') + return result + + def write_ly (self): + base = self.basename () + path = os.path.join (self.global_options.lily_output_dir, base) + directory = os.path.split(path)[0] + if not os.path.isdir (directory): + os.makedirs (directory) + + # First write the XML to a file (so we can link it!) + if self.compressed: + xmlfilename = path + '.mxl' + else: + xmlfilename = path + '.xml' + if os.path.exists (xmlfilename): + diff_against_existing = self.filter_pipe (self.contents, 'diff -u %s - ' % xmlfilename) + if diff_against_existing: + warning ("%s: duplicate filename but different contents of orginal file,\n\ +printing diff against existing file." % xmlfilename) + ly.stderr_write (diff_against_existing) + else: + out = file (xmlfilename, 'w') + out.write (self.contents) + out.close () + + # also write the converted lilypond + filename = path + '.ly' + if os.path.exists (filename): + diff_against_existing = self.filter_pipe (self.full_ly (), 'diff -u %s -' % filename) + if diff_against_existing: + warning ("%s: duplicate filename but different contents of converted lilypond file,\n\ +printing diff against existing file." % filename) + ly.stderr_write (diff_against_existing) + else: + out = file (filename, 'w') + out.write (self.full_ly ()) + out.close () + + class LilyPondVersionString (Snippet): """A string that does not require extra memory.""" def __init__ (self, type, match, formatter, line_number, global_options): @@ -851,4 +951,5 @@ snippet_type_to_class = { 'lilypond': LilypondSnippet, 'include': IncludeSnippet, 'lilypondversion': LilyPondVersionString, + 'musicxml_file': MusicXMLFileSnippet, } diff --git a/python/book_texinfo.py b/python/book_texinfo.py index 03f723754a..9399451647 100644 --- a/python/book_texinfo.py +++ b/python/book_texinfo.py @@ -58,6 +58,15 @@ TexInfo_snippet_res = { .*? @end\s+ignore))\s''', + 'musicxml_file': r'''(?mx) + ^(?P + @musicxmlfile\s*( + \[ + \s*(?P.*?)\s* + \])?\s*{ + (?P\S+) + })''', + 'singleline_comment': r'''(?mx) ^.* (?P @@ -103,7 +112,7 @@ TexInfo_output = { @end ifinfo @html

- + + @end html @file{%(filename)s} @html