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: Thomas Morley
Subject: Re: calculation of the total duration of a score
Date: Thu, 8 Sep 2016 10:02:44 +0200

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



reply via email to

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