%%%% 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))) } %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% }