lilypond-user
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: calculation of the total duration of a score


From: Marc Hohl
Subject: Re: calculation of the total duration of a score
Date: Thu, 8 Sep 2016 14:21:04 +0200
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Thunderbird/45.2.0

Hi Harm,

thanks a lot for your engraver, works out of the box!

Best regards,

Marc

Am 08.09.2016 um 10:02 schrieb Thomas Morley:
2016-09-08 5:39 GMT+02:00 Paul <address@hidden>:
On 09/05/2016 05:01 AM, Marc Hohl wrote:

Has someone else already done something like this? I have no experience
in writing scheme engravers, so any hint would be highly appreciated.


lilypond-html-live-score does something like this by post-processing an SVG
with python to add meta data to the grobs in the SVG file:
https://gitlab.com/sigmate/lilypond-html-live-score/blob/master/make-live-score#L88

I was working on porting part of that to scheme so it could be done
directly.  I've attached my include file.  It will currently display the
total duration to the log.  It could be simplified in various ways if it was
calculating just the total duration and not adding timing info to every
grob.

It's still a work in progress and only lightly tested.

-Paul



Hi,

I've seen Paul answered already, had no time to look into his code, though.

Below my own approach.
Disadvantage: The duration-indication is bound to the last seen event.
This may result in different positions of the RehearsalMark, which I
prefered over TextScript (but this special problem would happen for
both)

\version "2.19.47"

#(define (get-seconds lst rl)
  "Takes a list of kind
'((#<Mom 17> 1/15)
  (#<Mom 31/2> 1/30)
  (#<Mom 0> 1/15))
Calculates the time passed between each moment.
Returns the addition of it as an exact numerical value.
  "
  (if (null? (cdr lst))
      (apply + rl)
      (get-seconds
        (cdr lst)
        (cons
          (* (cdr (cadr lst))
             (ly:moment-main (ly:moment-sub (caar lst) (caadr lst))))
          rl))))


#(define (score-duration-engraver context)
  (let* ((evts '())
         (last-evt #f)
         (grobs '())
         (tempo-change-evts '()))
  (make-engraver
    (listeners
      ((rhythmic-event engraver event)
        (set! last-evt (ly:event-property event 'length))
        (set! evts (cons (ly:context-current-moment context) evts))
        ;; TODO creating RehearsalMarks at every rhythmic-event looks like
        ;; a huge waste. How to do it better?
        (set! grobs
              (cons
                (ly:engraver-make-grob engraver 'RehearsalMark event)
                grobs)))
      ((tempo-change-event engraver event)
       (let ((tempo-unit
               ;; Hmm, ugly code...
               (string->number
                 (ly:duration->string (ly:event-property event 'tempo-unit))))
             (metronome-count (ly:event-property event 'metronome-count)))
        ;; Accumulate pairs of "moment when it happens" and
        ;; "quotient of tempo-unit and metronome-count"in `tempo-change-evts'
        ;; for use in `get-seconds'
        (set! tempo-change-evts
          (cons
            (cons
              (ly:context-current-moment context)
              (/ tempo-unit metronome-count))
            tempo-change-evts)))))
    ((finalize translator)
     (let* (;; add default tempo, if not introduced at score-begin
            (tempo-changes
              (if (null? tempo-change-evts)
                  (list (cons (ly:make-moment 0) 1/15))
                  (if (not
                        (equal? (ly:make-moment 0)
                                (car (last tempo-change-evts))))
                      (append
                        tempo-change-evts (list (cons (ly:make-moment 0) 1/15)))
                      tempo-change-evts)))
            (duration-before-last-tempo-change
              (get-seconds tempo-changes '()))
            (duration-after-last-tempo-change-without-last-dur
              (* (cdr (car tempo-changes))
                 (ly:moment-main
                   (ly:moment-sub (car evts) (caar tempo-changes)))))
            (last-ev-duration
              (* (cdar tempo-changes) (ly:moment-main last-evt)))
            (final-duration
              (+
                 duration-before-last-tempo-change
                 duration-after-last-tempo-change-without-last-dur
                 last-ev-duration))
            (minutes (floor final-duration))
            ;; Is using floor correct?
            (seconds (floor (* (- final-duration minutes ) 60)))
            (duration-string
              (format #f "Duration: ~a:~2,,,'address@hidden" minutes seconds)))
     ;; Only keep the last created RehearsalMark, suicide the others
     (for-each ly:grob-suicide! (cdr grobs))
     (ly:grob-set-property! (first grobs) 'direction DOWN)
     (ly:grob-set-property! (first grobs) 'text
        ;; a little custom-formatting
        (markup #:rounded-box #:fontsize -3 duration-string))
     (set! evts '())
     (set! last-evt #f)
     (set! grobs '())
     (set! tempo-change-evts '()))))))

\layout {
  \context {
        \Score
        \consists \score-duration-engraver
  }
}

%%%%%%%%%%%%%%%%%%%%%%
%% EXAMPLE
%%%%%%%%%%%%%%%%%%%%%%

voiceI =
  \new Voice {
    \partial 4
    c'4
    \repeat unfold 61 c'4
    \tempo 4=120
    c'2. d'2
    \tempo 8=120
    c'2~ |
    c'1
  }

voiceII = { \partial 4 cis'4 \repeat unfold 18 cis'1 }

\score {
  <<
      \voiceI
      \voiceII
  >>
  \layout { }
  \midi {}
}

Cheers,
  Harm

_______________________________________________
lilypond-user mailing list
address@hidden
https://lists.gnu.org/mailman/listinfo/lilypond-user





reply via email to

[Prev in Thread] Current Thread [Next in Thread]