From 81b5ad4f11cdb296c69fcd2259effbc75a3c9054 Mon Sep 17 00:00:00 2001 From: Reinhold Kainhofer Date: Fri, 11 Jun 2010 21:19:19 +0200 Subject: [PATCH] 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... --- .../html-musicxml-file-compressed.htmly | 6 + .../html-musicxml-file-options.htmly | 6 + .../lilypond-book/html-musicxml-file.htmly | 6 + input/regression/lilypond-book/include.mxl | Bin 0 -> 28589 bytes input/regression/lilypond-book/include.xml | 55 +++++++++ .../tex-musicxml-file-options.lytex | 5 + .../lilypond-book/tex-musicxml-file.lytex | 5 + .../texinfo-musicxml-file-options.tely | 12 ++ .../lilypond-book/texinfo-musicxml-file.tely | 12 ++ python/book_base.py | 3 +- python/book_docbook.py | 2 +- python/book_html.py | 13 +- python/book_latex.py | 11 ++ python/book_snippets.py | 111 +++++++++++++++++- python/book_texinfo.py | 13 +- 15 files changed, 249 insertions(+), 11 deletions(-) create mode 100644 input/regression/lilypond-book/html-musicxml-file-compressed.htmly create mode 100644 input/regression/lilypond-book/html-musicxml-file-options.htmly create mode 100644 input/regression/lilypond-book/html-musicxml-file.htmly create mode 100644 input/regression/lilypond-book/include.mxl create mode 100644 input/regression/lilypond-book/include.xml create mode 100644 input/regression/lilypond-book/tex-musicxml-file-options.lytex create mode 100644 input/regression/lilypond-book/tex-musicxml-file.lytex create mode 100644 input/regression/lilypond-book/texinfo-musicxml-file-options.tely create mode 100644 input/regression/lilypond-book/texinfo-musicxml-file.tely 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 0000000000000000000000000000000000000000..d7090944879500ac86e70ed9e46f6242e809cee1 GIT binary patch literal 28589 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 HKX3mFTAG15 literal 0 HcmV?d00001 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 -- 2.39.2