1 ;;;; to-xml.scm -- dump parse tree as xml
3 ;;;; source file of the GNU LilyPond music typesetter
5 ;;;; (c) 2003--2004 Han-Wen Nienhuys <hanwen@cs.uu.nl>
6 ;;;; Jan Nieuwenhuizen <janneke@gnu.org>
8 (use-modules (ice-9 regex)
13 Todo: this is a quick hack; it makes more sense to define a GOOPS
14 class of a documentnode (similar to how
15 ; the documentation is generated.)
17 That is much cleaner: building the document, and dumping it to output
32 (define-class <xml-node> ()
33 (name #:init-value "" #:accessor node-name #:init-keyword #:name)
34 (value #:init-value "" #:accessor node-value #:init-keyword #:value)
35 (attributes #:init-value '()
36 #:accessor node-attributes
37 #:init-keyword #:attributes)
38 (children #:init-value '()
39 #:accessor node-children
40 #:init-keyword #:children))
44 (SequentialMusic . measure)
53 (define (musicxml-node->string node)
54 (let ((xml-name (assoc-get (node-name node) node-names #f)))
56 (if xml-name (open-tag xml-name '() '()) "")
57 (if (equal? (node-value node) "")
60 (apply string-append (map musicxml-node->string (node-children node))))
62 (if xml-name (close-tag xml-name) "")
63 (if xml-name "\n" ""))))
65 (define (xml-node->string node)
68 (open-tag (node-name node) (node-attributes node) '())
69 (if (equal? (node-value node) "")
71 (apply string-append (map xml-node->string (node-children node))))
74 (close-tag (node-name node))))
76 (define (musicxml-duration->xml-node d)
79 #:value (number->string (ash 1 (ly:duration-log d)))))
81 (define (duration->xml-node d)
84 ;; #:value (number->string (ash 1 (ly:duration-log d)))))
85 #:attributes `((log . ,(ly:duration-log d))
86 (dots . ,(ly:duration-dot-count d))
87 (numer . ,(car (ly:duration-factor d)))
88 (denom . ,(cdr (ly:duration-factor d))))))
90 (define (musicxml-pitch->xml-node p)
97 #:value (list-ref '("C" "D" "E" "F" "G" "A" "B")
98 (ly:pitch-notename p)))
101 #:value (number->string (ly:pitch-octave p))))))
103 (define (pitch->xml-node p)
106 #:attributes `((octave . ,(ly:pitch-octave p))
107 (notename . ,(ly:pitch-notename p))
108 (alteration . ,(ly:pitch-alteration p)))))
110 (define (music->xml-node music)
111 (let* ((name (ly:music-property music 'name))
112 (e (ly:music-property music 'element))
113 (es (ly:music-property music 'elements))
114 (mprops (ly:music-mutable-properties music))
115 (d (ly:music-property music 'duration))
116 (p (ly:music-property music 'pitch))
117 (ignore-props '(origin elements duration pitch element)))
124 (if (ly:pitch? p) (list (pitch->xml-node p)) '())
125 (if (ly:duration? d) (list (duration->xml-node d)) '())
126 (if (pair? es) (map music->xml-node es) '())
127 (if (ly:music? e) (list (music->xml-node e)) '())
132 "<?xml version=\"1.0\"?>
141 ;; as computed from input/trip.ly, by
142 ;; http://www.pault.com/pault/dtdgenerator/
144 ;; must recompute with larger, more serious piece, and probably
145 ;; manually add stuff
146 (define preliminary-dtd
148 <!ELEMENT duration EMPTY >
149 <!ATTLIST duration denom ( 1 | 3 | 5 ) #REQUIRED >
150 <!ATTLIST duration dots ( 0 | 1 ) #REQUIRED >
151 <!ATTLIST duration log ( 0 | 1 | 2 | 3 | 4 ) #REQUIRED >
152 <!ATTLIST duration numer ( 1 | 4 ) #REQUIRED >
154 <!ELEMENT music ( duration | music | pitch )* >
155 <!ATTLIST music articulation-type ( lheel | ltoe | marcato | rheel | rtoe | staccato | tenuto ) #IMPLIED >
156 <!ATTLIST music change-to-id NMTOKEN #IMPLIED >
157 <!ATTLIST music change-to-type NMTOKEN #IMPLIED >
158 <!ATTLIST music context-id CDATA #IMPLIED >
159 <!ATTLIST music context-type ( PianoStaff | Score | Staff | Timing | Voice ) #IMPLIED >
160 <!ATTLIST music denominator NMTOKEN #IMPLIED >
161 <!ATTLIST music direction ( 0 | 1 ) #IMPLIED >
162 <!ATTLIST music force-accidental CDATA #IMPLIED >
163 <!ATTLIST music grob-property NMTOKEN #IMPLIED >
164 <!ATTLIST music grob-value CDATA #IMPLIED >
165 <!ATTLIST music iterator-ctor CDATA #IMPLIED >
166 <!ATTLIST music label NMTOKEN #IMPLIED >
167 <!ATTLIST music last-pitch CDATA #IMPLIED >
168 <!ATTLIST music numerator NMTOKEN #IMPLIED >
169 <!ATTLIST music penalty NMTOKEN #IMPLIED >
170 <!ATTLIST music pitch-alist CDATA #IMPLIED >
171 <!ATTLIST music pop-first CDATA #IMPLIED >
172 <!ATTLIST music repeat-count NMTOKEN #IMPLIED >
173 <!ATTLIST music span-direction ( -1 | 1 ) #IMPLIED >
174 <!ATTLIST music span-type NMTOKEN #IMPLIED >
175 <!ATTLIST music symbol NMTOKEN #IMPLIED >
176 <!ATTLIST music text NMTOKEN #IMPLIED >
177 <!ATTLIST music text-type NMTOKEN #IMPLIED >
178 <!ATTLIST music type NMTOKEN #REQUIRED >
179 <!ATTLIST music value CDATA #IMPLIED >
181 <!ELEMENT pitch EMPTY >
182 <!ATTLIST pitch alteration ( 0 | 1 ) #REQUIRED >
183 <!ATTLIST pitch notename ( 0 | 1 | 2 | 3 | 4 | 5 | 6 ) #REQUIRED >
184 <!ATTLIST pitch octave ( -1 | -2 | 0 | 1 ) #REQUIRED >")
191 (error "assertion failed")))
193 (define (re-sub re to string)
194 (regexp-substitute/global #f re string 'pre to 'post))
196 (define (re-sub-alist string alist)
199 (re-sub (caar alist) (cdar alist)
200 (re-sub-alist string (cdr alist)))))
202 (define xml-entities-alist
209 (define (open-tag tag attrs exceptions)
210 (define (candidate? x)
211 (not (memq (car x) exceptions)))
213 (define (dump-attr sym-val)
224 (let ((s (call-with-output-string (lambda (port) (display val port)))))
225 (re-sub-alist s xml-entities-alist))
230 "<" (symbol->string tag)
231 (apply string-append (map dump-attr (filter candidate? attrs)))
234 (define (close-tag name)
235 (string-append "</" (symbol->string name) ">"))
237 (define-public (music-to-xml music port)
238 "Dump XML-ish stuff to PORT."
240 ;; dtd contains # -- This confuses tex during make web.
242 ;; (display (dtd-header) port)
244 (display (open-tag 'music '((type . score)) '()) port)
245 (display (xml-node->string (music->xml-node music)) port)
246 (display (close-tag 'music) port))
248 (define-public (music-to-musicxml music port)
249 "Dump MusicXML-ish stuff to PORT."
251 ;; dtd contains # -- This confuses tex during make web.
253 ;; (display (dtd-header) port)
255 (define pitch->xml-node musicxml-pitch->xml-node)
256 (define duration->xml-node musicxml-duration->xml-node)
258 (display (open-tag 'music '((type . score)) '()) port)
259 (display (musicxml-node->string (music->xml-node music)) port)
260 (display (close-tag 'music) port))