1 %%%% This file is part of LilyPond, the GNU music typesetter.
3 %%%% Copyright (C) 2011 Graham Percival <graham@percival-music.ca>
5 %%%% LilyPond is free software: you can redistribute it and/or modify
6 %%%% it under the terms of the GNU General Public License as published by
7 %%%% the Free Software Foundation, either version 3 of the License, or
8 %%%% (at your option) any later version.
10 %%%% LilyPond is distributed in the hope that it will be useful,
11 %%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
12 %%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 %%%% GNU General Public License for more details.
15 %%%% You should have received a copy of the GNU General Public License
16 %%%% along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
20 % This file is used for Vivi, the Virtual Violinist:
21 % http://percival-music.ca/vivi.html
22 % but it may be helpful to other researchers, either with the same
23 % output, or as a basis for other work in extracting music events
26 % Output format is tab-separated lines, like this:
27 %0.00000000 note 57 0.25000000 point-and-click 2 38
29 %0.25000000 note 62 0.25000000 point-and-click 7 38
30 %0.50000000 note 66 0.12500000 point-and-click 9 38
31 %0.50000000 script staccato
39 #(define (filename-from-staffname engraver)
40 "Constructs a filename in the form
41 @file{@var{original_filename}-@var{staff_instrument_name}.notes} if the
42 staff has an instrument name. If the staff has no instrument
43 name, it uses "unnamed-staff" for that part of the filename."
44 (let* ((inst-name (ly:context-property
45 (ly:translator-context engraver)
47 (string-concatenate (list
48 (substring (object->string (command-line))
49 ;; filename without .ly part
50 (+ (string-rindex (object->string (command-line)) #\sp) 2)
51 (- (string-length (object->string (command-line))) 5))
53 (if (string? inst-name)
58 #(define (format-moment moment)
60 (/ (ly:moment-main-numerator moment)
61 (ly:moment-main-denominator moment))))
63 #(define (moment-grace->string moment)
64 "Prints a moment without grace note(s) as a float such as
65 0.25000. Grace notes are written with the grace duration as a
66 separate \"dashed\" number, i.e. 0.25000-0.12500. This allows any
67 program using the output of this function to interpret grace notes
68 however they want (half duration, quarter duration? before beat,
71 (eq? 0 (ly:moment-grace-numerator moment))
72 (ly:format "~a" (format-moment moment))
73 ;; grace notes have a negative numerator, so no "-" necessary
76 (format-moment moment)
79 (ly:moment-grace-numerator moment)
80 (ly:moment-grace-denominator moment))))))
82 #(define (make-output-string-line engraver values)
83 "Constructs a tab-separated string beginning with the
84 score time (derived from the engraver) and then adding all the
85 values. The string ends with a newline."
86 (let* ((context (ly:translator-context engraver))
87 (moment (ly:context-current-moment context)))
91 (list (moment-grace->string moment))
93 (lambda (x) (ly:format "~a" x))
99 #(define (print-line engraver . values)
100 "Prints the list of values (plus the score time) to a file, and
101 optionally outputs to the console as well."
102 (let* ((p (open-file (filename-from-staffname engraver) "a")))
103 ;; for regtest comparison
104 (if (defined? 'EVENT_LISTENER_CONSOLE_OUTPUT)
106 (make-output-string-line engraver values)))
108 (make-output-string-line engraver values)
115 #(define (format-rest engraver event)
119 (ly:event-property event 'duration))
120 (format-moment (ly:duration-length
121 (ly:event-property event 'duration)))))
123 #(define (format-note engraver event)
124 (let* ((origin (ly:input-file-line-char-column
125 (ly:event-property event 'origin))))
128 ;; get a MIDI pitch value.
129 (+ 60 (ly:pitch-semitones
130 (ly:event-property event 'pitch)))
132 (ly:event-property event 'duration))
133 (format-moment (ly:duration-length
134 (ly:event-property event 'duration)))
135 ;; point and click info
136 (ly:format "point-and-click ~a ~a"
140 #(define (format-tempo engraver event)
143 ; get length of quarter notes, in seconds
144 (/ (ly:event-property event 'metronome-count)
145 (format-moment (ly:duration-length (ly:event-property
150 #(define (format-breathe engraver event)
154 #(define (format-tie engraver event)
158 #(define (format-articulation engraver event)
161 (ly:event-property event 'articulation-type)))
163 #(define (format-text engraver event)
166 (ly:event-property event 'text)))
168 #(define (format-slur engraver event)
171 (ly:event-property event 'span-direction)))
173 #(define (format-dynamic engraver event)
176 (ly:event-property event 'text)))
178 #(define (format-cresc engraver event)
181 (ly:event-property event 'span-direction)))
183 #(define (format-decresc engraver event)
186 (ly:event-property event 'span-direction)))
188 #(define (format-textspan engraver event)
189 (let* ((context (ly:translator-context engraver))
190 (moment (ly:context-current-moment context))
191 (spanner-props (ly:context-property context 'TextSpanner))
192 (details (chain-assoc-get 'bound-details spanner-props))
193 (left-props (assoc-get 'left details '()))
194 (left-text (assoc-get 'text left-props '())))
197 (ly:event-property event 'span-direction)
201 %%%% The actual engraver definition: We just install some listeners so we
202 %%%% are notified about all notes and rests. We don't create any grobs or
203 %%%% change any settings.
211 (cons 'tempo-change-event format-tempo)
212 (cons 'rest-event format-rest)
213 (cons 'note-event format-note)
214 (cons 'articulation-event format-articulation)
215 (cons 'text-script-event format-text)
216 (cons 'slur-event format-slur)
217 (cons 'breathing-event format-breathe)
218 (cons 'dynamic-event format-dynamic)
219 (cons 'crescendo-event format-cresc)
220 (cons 'decrescendo-event format-decresc)
221 (cons 'text-span-event format-textspan)
222 (cons 'tie-event format-tie)