%%%% This file is part of LilyPond, the GNU music typesetter.
%%%%
%%%% Copyright (C) 2011--2012 Graham Percival
%%%%
%%%% LilyPond is free software: you can redistribute it and/or modify
%%%% it under the terms of the GNU General Public License as published by
%%%% the Free Software Foundation, either version 3 of the License, or
%%%% (at your option) any later version.
%%%%
%%%% LilyPond is distributed in the hope that it will be useful,
%%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
%%%% GNU General Public License for more details.
%%%%
%%%% You should have received a copy of the GNU General Public License
%%%% along with LilyPond. If not, see .
%
%
%
% This file is used for Vivi, the Virtual Violinist:
% http://percival-music.ca/vivi.html
% but it may be helpful to other researchers, either with the same
% output, or as a basis for other work in extracting music events
% from lilypond.
%
% Output format is tab-separated lines, like this:
%0.00000000 note 57 0.25000000 point-and-click 2 38
%0.00000000 dynamic f
%0.25000000 note 62 0.25000000 point-and-click 7 38
%0.50000000 note 66 0.12500000 point-and-click 9 38
%0.50000000 script staccato
\version "2.16.0"
%%%% Helper functions
#(define (filename-from-staffname context)
"Constructs a filename in the form
@address@hidden@var{staff_instrument_name}.notes} if the
staff has an instrument name. If the staff has no instrument
name, it uses "unnamed-staff" for that part of the filename."
(let* ((inst-name (ly:context-property context 'instrumentName)))
(string-concatenate (list
(substring (object->string (command-line))
;; filename without .ly part
(+ (string-rindex (object->string (command-line)) #\sp) 2)
(- (string-length (object->string (command-line))) 5))
"-"
(if (string? inst-name)
inst-name
"unnamed-staff")
".notes"))))
#(define (format-moment moment)
(exact->inexact
(/ (ly:moment-main-numerator moment)
(ly:moment-main-denominator moment))))
#(define (moment-grace->string moment)
"Prints a moment without grace note(s) as a float such as
0.25000. Grace notes are written with the grace duration as a
separate \"dashed\" number, i.e. 0.25000-0.12500. This allows any
program using the output of this function to interpret grace notes
however they want (half duration, quarter duration? before beat,
after beat? etc.)."
(if
(eq? 0 (ly:moment-grace-numerator moment))
(ly:format "~a" (format-moment moment))
;; grace notes have a negative numerator, so no "-" necessary
(ly:format
"~a~a"
(format-moment moment)
(format-moment
(ly:make-moment
(ly:moment-grace-numerator moment)
(ly:moment-grace-denominator moment))))))
#(define (make-output-string-line context values)
"Constructs a tab-separated string beginning with the
score time (derived from the context) and then adding all the
values. The string ends with a newline."
(let* ((moment (ly:context-current-moment context)))
(string-append
(string-join
(append
(list (moment-grace->string moment))
(map
(lambda (x) (ly:format "~a" x))
values))
"\t")
"\n")))
#(define (print-line context . values)
"Prints the list of values (plus the score time) to a file, and
optionally outputs to the console as well. context may be specified
as an engraver for convenience."
(if (ly:translator? context)
(set! context (ly:translator-context context)))
(let* ((p (open-file (filename-from-staffname context) "a")))
;; for regtest comparison
(if (defined? 'EVENT_LISTENER_CONSOLE_OUTPUT)
(ly:progress
(make-output-string-line context values)))
(display
(make-output-string-line context values)
p)
(close p)))
%%% main functions
#(define (format-rest engraver event)
(print-line engraver
"rest"
(ly:duration->string
(ly:event-property event 'duration))
(format-moment (ly:duration-length
(ly:event-property event 'duration)))))
#(define (format-note engraver event)
(let* ((origin (ly:input-file-line-char-column
(ly:event-property event 'origin))))
(print-line engraver
"note"
;; get a MIDI pitch value.
(+ 60 (ly:pitch-semitones
(ly:event-property event 'pitch)))
(ly:duration->string
(ly:event-property event 'duration))
(format-moment (ly:duration-length
(ly:event-property event 'duration)))
;; point and click info
(ly:format "point-and-click ~a ~a"
(caddr origin)
(cadr origin)))))
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ADDED %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
#(define (format-drumnote engraver event)
(let* ((origin (ly:input-file-line-char-column
(ly:event-property event 'origin))))
(print-line engraver
"note"
;; get a MIDI pitch value.
;; but with a DrumVoice I get:
;; In procedure ly:pitch-semitones in expression
;; (ly:pitch-semitones (ly:event-property event-chord #)):
;; Wrong type argument in position 1 (expecting Pitch): loconga
;(+ 60 (ly:pitch-semitones
(ly:event-property event 'drum-type) ;))
(ly:duration->string
(ly:event-property event 'duration))
(format-moment (ly:duration-length
(ly:event-property event 'duration)))
;; point and click info
(ly:format "point-and-click ~a ~a"
(caddr origin)
(cadr origin))
)))
#(define (format-chordnote engraver event-chord)
(let* ((origin (ly:input-file-line-char-column
(ly:event-property event-chord 'origin))))
(print-line engraver
"note"
;; get a MIDI pitch value.
(+ 60 (ly:pitch-semitones
(ly:event-property event-chord 'pitch)))
(ly:duration->string
(ly:event-property event-chord 'duration))
(format-moment (ly:duration-length
(ly:event-property event-chord 'duration)))
;; point and click info
(ly:format "point-and-click ~a ~a"
(caddr origin)
(cadr origin))
)))
#(define (format-chorddrumnote engraver event-chord)
(let* ((origin (ly:input-file-line-char-column
(ly:event-property event-chord 'origin))))
(print-line engraver
"note"
;; get a MIDI pitch value.
;; same as above, with a DrumVoice I get:
;; In procedure ly:pitch-semitones in expression
;; (ly:pitch-semitones (ly:event-property event-chord #)):
;; Wrong type argument in position 1 (expecting Pitch): loconga
;(+ 60 (ly:pitch-semitones
(ly:event-property event-chord 'drum-type) ;))
(ly:duration->string
(ly:event-property event-chord 'duration))
(format-moment (ly:duration-length
(ly:event-property event-chord 'duration)))
;; point and click info
(ly:format "point-and-click ~a ~a"
(caddr origin)
(cadr origin))
)))
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
#(define (format-tempo engraver event)
(print-line engraver
"tempo"
; get length of quarter notes, in seconds
(/ (ly:event-property event 'metronome-count)
(format-moment (ly:duration-length (ly:event-property
event
'tempo-unit))))))
#(define (format-breathe engraver event)
(print-line engraver
"breathe"))
#(define (format-glissando engraver event)
(print-line engraver
"gliss"))
#(define (format-tie engraver event)
(print-line engraver
"tie"))
#(define (format-articulation engraver event)
(print-line engraver
"script"
(ly:event-property event 'articulation-type)))
#(define (format-text engraver event)
(print-line engraver
"text"
(ly:event-property event 'text)))
#(define (format-slur engraver event)
(print-line engraver
"slur"
(ly:event-property event 'span-direction)))
#(define (format-dynamic engraver event)
(print-line engraver
"dynamic"
(ly:event-property event 'text)))
#(define (format-dynamic engraver event-chord)
(print-line engraver
"dynamic"
(ly:event-property event-chord 'text)
))
#(define (format-cresc engraver event)
(print-line engraver
"cresc"
(ly:event-property event 'span-direction)))
#(define (format-decresc engraver event)
(print-line engraver
"decresc"
(ly:event-property event 'span-direction)))
#(define (format-textspan engraver event)
(let* ((context (ly:translator-context engraver))
(moment (ly:context-current-moment context))
(spanner-props (ly:context-property context 'TextSpanner))
(details (chain-assoc-get 'bound-details spanner-props))
(left-props (assoc-get 'left details '()))
(left-text (assoc-get 'text left-props '())))
(print-line engraver
"set_string"
(ly:event-property event 'span-direction)
left-text)))
%%%% The actual engraver definition: We just install some listeners so we
%%%% are notified about all notes and rests. We don't create any grobs or
%%%% change any settings.
\layout {
\context {
\Voice
\consists #(make-engraver
(listeners
(tempo-change-event . format-tempo)
(rest-event . format-rest)
(note-event . format-note)
; Added: chord-event
(chord-event . format-chordnote)
(articulation-event . format-articulation)
(text-script-event . format-text)
(slur-event . format-slur)
(breathing-event . format-breathe)
(dynamic-event . format-dynamic)
(dynamic-event-chord . format-dynamic)
(crescendo-event . format-cresc)
(decrescendo-event . format-decresc)
(text-span-event . format-textspan)
(glissando-event . format-glissando)
(tie-event . format-tie)))
}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ADDED %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\context {
\DrumVoice
\consists #(make-engraver
(listeners
(tempo-change-event . format-tempo)
(rest-event . format-rest)
; Added: format-drumnote
(note-event . format-drumnote)
; Added: chord-event
(chord-event . format-chorddrumnote)
(articulation-event . format-articulation)
(text-script-event . format-text)
(slur-event . format-slur)
(breathing-event . format-breathe)
(dynamic-event . format-dynamic)
(dynamic-event-chord . format-dynamic)
(crescendo-event . format-cresc)
(decrescendo-event . format-decresc)
(text-span-event . format-textspan)
(glissando-event . format-glissando)
(tie-event . format-tie)))
}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
}