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;hp=f9b28c30a3badc92aaacbb70c56e5114fc1d77ab;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'''
''',
- 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