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 (adjust-for-grace moment)
64 "Adjusts any moment with a grace note by subtracting half of
65 the grace note duration. For example, an eighth note grace note
66 which would otherwise occur at score time 0.5 will now occur at
69 (eq? 0 (ly:moment-grace-numerator moment))
71 ;; get moment including grace note
72 ;; grace notes have a negative numerator, so add
74 ;; make the "grace duration" half as long
78 (ly:moment-grace-numerator moment)
79 (ly:moment-grace-denominator moment))))))
81 #(define (get-moment moment)
82 (format-moment (adjust-for-grace
85 #(define (make-output-string-line engraver values)
86 "Constructs a tab-separated string beginning with the
87 score time (derived from the engraver) and then adding all the
88 values. The string ends with a newline."
89 (let* ((context (ly:translator-context engraver))
90 (moment (ly:context-current-moment context)))
94 (lambda (x) (ly:format "~a" x))
96 (list (get-moment moment))
102 #(define (print-line engraver . values)
103 "Prints the list of values (plus the score time) to a file, and
104 optionally outputs to the console as well."
105 (let* ((p (open-file (filename-from-staffname engraver) "a")))
106 ;; for regtest comparison
107 (if (defined? 'EVENT_LISTENER_CONSOLE_OUTPUT)
109 (make-output-string-line engraver values)))
111 (make-output-string-line engraver values)
118 #(define (format-rest engraver event)
122 (ly:event-property event 'duration))))
124 #(define (format-note engraver event)
125 (let* ((origin (ly:input-file-line-char-column
126 (ly:event-property event 'origin))))
129 ;; get a MIDI pitch value.
130 (+ 60 (ly:pitch-semitones
131 (ly:event-property event 'pitch)))
132 (format-moment (ly:duration-length
133 (ly:event-property event 'duration)))
134 ;; point and click info
135 (ly:format "point-and-click ~a ~a"
139 #(define (format-tempo engraver event)
142 ; get length of quarter notes, in seconds
143 (/ (ly:event-property event 'metronome-count)
144 (format-moment (ly:duration-length (ly:event-property
149 #(define (format-breathe engraver event)
153 #(define (format-articulation engraver event)
156 (ly:event-property event 'articulation-type)))
158 #(define (format-text engraver event)
161 (ly:event-property event 'text)))
163 #(define (format-slur engraver event)
166 (ly:event-property event 'span-direction)))
168 #(define (format-dynamic engraver event)
171 (ly:event-property event 'text)))
173 #(define (format-cresc engraver event)
176 (ly:event-property event 'span-direction)))
178 #(define (format-decresc engraver event)
181 (ly:event-property event 'span-direction)))
183 #(define (format-textspan engraver event)
184 (let* ((context (ly:translator-context engraver))
185 (moment (ly:context-current-moment context))
186 (spanner-props (ly:context-property context 'TextSpanner))
187 (details (chain-assoc-get 'bound-details spanner-props))
188 (left-props (assoc-get 'left details '()))
189 (left-text (assoc-get 'text left-props '())))
192 (ly:event-property event 'span-direction)
196 %%%% The actual engraver definition: We just install some listeners so we
197 %%%% are notified about all notes and rests. We don't create any grobs or
198 %%%% change any settings.
206 (cons 'tempo-change-event format-tempo)
207 (cons 'rest-event format-rest)
208 (cons 'note-event format-note)
209 (cons 'articulation-event format-articulation)
210 (cons 'text-script-event format-text)
211 (cons 'slur-event format-slur)
212 (cons 'breathing-event format-breathe)
213 (cons 'dynamic-event format-dynamic)
214 (cons 'crescendo-event format-cresc)
215 (cons 'decrescendo-event format-decresc)
216 (cons 'text-span-event format-textspan)