1 %%%% This file is part of LilyPond, the GNU music typesetter.
3 %%%% Copyright (C) 2011--2012 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 context)
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 context 'instrumentName)))
45 (string-concatenate (list
46 (substring (object->string (command-line))
47 ;; filename without .ly part
48 (+ (string-rindex (object->string (command-line)) #\sp) 2)
49 (- (string-length (object->string (command-line))) 5))
51 (if (string? inst-name)
56 #(define (format-moment moment)
58 (/ (ly:moment-main-numerator moment)
59 (ly:moment-main-denominator moment))))
61 #(define (moment-grace->string moment)
62 "Prints a moment without grace note(s) as a float such as
63 0.25000. Grace notes are written with the grace duration as a
64 separate \"dashed\" number, i.e. 0.25000-0.12500. This allows any
65 program using the output of this function to interpret grace notes
66 however they want (half duration, quarter duration? before beat,
69 (zero? (ly:moment-grace-numerator moment))
70 (ly:format "~a" (format-moment moment))
71 ;; grace notes have a negative numerator, so no "-" necessary
74 (format-moment moment)
77 (ly:moment-grace-numerator moment)
78 (ly:moment-grace-denominator moment))))))
80 #(define (make-output-string-line context values)
81 "Constructs a tab-separated string beginning with the
82 score time (derived from the context) and then adding all the
83 values. The string ends with a newline."
84 (let* ((moment (ly:context-current-moment context)))
88 (list (moment-grace->string moment))
90 (lambda (x) (ly:format "~a" x))
96 #(define (print-line context . values)
97 "Prints the list of values (plus the score time) to a file, and
98 optionally outputs to the console as well. context may be specified
99 as an engraver for convenience."
100 (if (ly:translator? context)
101 (set! context (ly:translator-context context)))
102 (let* ((p (open-file (filename-from-staffname context) "a")))
103 ;; for regtest comparison
104 (if (defined? 'EVENT_LISTENER_CONSOLE_OUTPUT)
106 (make-output-string-line context values)))
108 (make-output-string-line context 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-glissando engraver event)
158 #(define (format-tie engraver event)
162 #(define (format-articulation engraver event)
165 (ly:event-property event 'articulation-type)))
167 #(define (format-text engraver event)
170 (ly:event-property event 'text)))
172 #(define (format-slur engraver event)
175 (ly:event-property event 'span-direction)))
177 #(define (format-dynamic engraver event)
180 (ly:event-property event 'text)))
182 #(define (format-cresc engraver event)
185 (ly:event-property event 'span-direction)))
187 #(define (format-decresc engraver event)
190 (ly:event-property event 'span-direction)))
192 #(define (format-textspan engraver event)
193 (let* ((context (ly:translator-context engraver))
194 (moment (ly:context-current-moment context))
195 (spanner-props (ly:context-property context 'TextSpanner))
196 (details (chain-assoc-get 'bound-details spanner-props))
197 (left-props (assoc-get 'left details '()))
198 (left-text (assoc-get 'text left-props '())))
201 (ly:event-property event 'span-direction)
205 %%%% The actual engraver definition: We just install some listeners so we
206 %%%% are notified about all notes and rests. We don't create any grobs or
207 %%%% change any settings.
212 \consists #(make-engraver
214 (tempo-change-event . format-tempo)
215 (rest-event . format-rest)
216 (note-event . format-note)
217 (articulation-event . format-articulation)
218 (text-script-event . format-text)
219 (slur-event . format-slur)
220 (breathing-event . format-breathe)
221 (dynamic-event . format-dynamic)
222 (crescendo-event . format-cresc)
223 (decrescendo-event . format-decresc)
224 (text-span-event . format-textspan)
225 (glissando-event . format-glissando)
226 (tie-event . format-tie)))