]> git.donarmstrong.com Git - lilypond.git/blob - ly/event-listener.ly
Event listener to extract (some) music events.
[lilypond.git] / ly / event-listener.ly
1 %%%% This file is part of LilyPond, the GNU music typesetter.
2 %%%%
3 %%%% Copyright (C) 2011 Graham Percival <graham@percival-music.ca>
4 %%%%
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.
9 %%%%
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.
14 %%%%
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/>.
17 %
18 %
19 %
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
24 % from lilypond.
25 %
26 % Output format is tab-separated lines, like this:
27 %0.00000000     note    57      0.25000000      point-and-click 2 38
28 %0.00000000     dynamic f
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
32
33
34
35 \version "2.13.57"
36
37 %%%% Helper functions
38
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)
46                       'instrumentName)))
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))
52                           "-"
53                           (if (string? inst-name)
54                               inst-name
55                             "unnamed-staff")
56                           ".notes"))))
57
58 #(define (format-moment moment)
59    (exact->inexact
60     (/ (ly:moment-main-numerator moment)
61        (ly:moment-main-denominator moment))))
62
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
67 score time 0.375."
68    (if
69        (eq? 0 (ly:moment-grace-numerator moment))
70        moment
71        ;; get moment including grace note
72        ;; grace notes have a negative numerator, so add
73        (ly:moment-add moment
74                       ;; make the "grace duration" half as long
75                       (ly:moment-mul
76                        (ly:make-moment 1 2)
77                        (ly:make-moment
78                         (ly:moment-grace-numerator moment)
79                         (ly:moment-grace-denominator moment))))))
80
81 #(define (get-moment moment)
82    (format-moment (adjust-for-grace
83                    moment)))
84
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)))
91     (string-append
92      (string-join
93       (map
94        (lambda (x) (ly:format "~a" x))
95         (append
96          (list (get-moment moment))
97           values))
98         "\t")
99      "\n")))
100
101
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)
108      (ly:progress
109       (make-output-string-line engraver values)))
110     (display
111      (make-output-string-line engraver values)
112      p)
113     (close p)))
114
115
116 %%% main functions
117
118 #(define (format-rest engraver event)
119    (print-line engraver
120                "rest"
121                (ly:duration->string
122                 (ly:event-property event 'duration))))
123
124 #(define (format-note engraver event)
125    (let* ((origin (ly:input-file-line-char-column
126                    (ly:event-property event 'origin))))
127      (print-line engraver
128                  "note"
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"
136                             (caddr origin)
137                             (cadr origin)))))
138
139 #(define (format-tempo engraver event)
140    (print-line engraver
141                "tempo"
142                ; get length of quarter notes, in seconds
143                (/ (ly:event-property event 'metronome-count)
144                    (format-moment (ly:duration-length (ly:event-property
145                                                        event
146                                                        'tempo-unit))))))
147
148
149 #(define (format-breathe engraver event)
150    (print-line engraver
151                "breathe"))
152
153 #(define (format-articulation engraver event)
154    (print-line engraver
155                "script"
156                (ly:event-property event 'articulation-type)))
157
158 #(define (format-text engraver event)
159    (print-line engraver
160                "text"
161                (ly:event-property event 'text)))
162
163 #(define (format-slur engraver event)
164    (print-line engraver
165                "slur"
166                (ly:event-property event 'span-direction)))
167
168 #(define (format-dynamic engraver event)
169    (print-line engraver
170                "dynamic"
171                (ly:event-property event 'text)))
172
173 #(define (format-cresc engraver event)
174    (print-line engraver
175                "cresc"
176                (ly:event-property event 'span-direction)))
177
178 #(define (format-decresc engraver event)
179    (print-line engraver
180                "decresc"
181                (ly:event-property event 'span-direction)))
182
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 '())))
190      (print-line engraver
191                  "set_string"
192                  (ly:event-property event 'span-direction)
193                  left-text)))
194
195
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.
199
200 \layout {
201   \context {
202   \Voice
203   \consists #(list
204               (cons 'listeners
205                     (list
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)
217                      )))
218   }
219 }