Re: calculation of the total duration of a score
Hi Jan-Peter, Am 16.09.2016 um 08:40 schrieb Jan-Peter Voigt: Hi Marc, nice to hear, that you have a workflow. For the record I noted two things in the file. (And if there's some spare time, I might further work on it.) Thanks for the continuing work on that issue! 1. The durations are tagged by score and stored in an a-list, so you can first collect durations in multiple scores and display them later with a score-unique tag. The tagging works fine, but as soon as I have a layout block in the \score creating the exact midi file, lilypond prints it :-( Moving the callback from \layout {} to \midi {} yields to Programming error: Cannot find key `scoreBACH' in alist, setting to `0'. So I still have to use my solution with the auxiliary files, which is no problem at all. > 2. There are acknowledgers for volta-brackets and barlines. It should > be possible to use that information for acknowledging volta repeats > and ajusting the duration accordingly. Those grobs are created by > engravers, but if one creates the barlines *not* with \repeat volta, > but with \bar ".|:", it should be acknowledged too. This sounds promising, but IMHO it would be easier to get the engraver to work somehow on a \score with a \midi { } block only and transferring the duration to another \score. Cheers, Marc Cheers Jan-Peter Am 15.09.2016 um 08:10 schrieb Marc Hohl: Am 14.09.2016 um 14:24 schrieb Marc Hohl: [...] I thought of writing the duration to a external file to be read from within the markup call in the score to be printed, but did not follow this route any further yet. Update: a simple test file shows that this works. I can - compute the total duration of a file designed for midi output only by means of your engraver - write the duration to a file and - read that string while processing the "print-only" file. Merging that stuff together with some Makefile should be straightforward, I hope ;-) And it has the advantage that some MIDI-related changes do not appear in the printed score, but the duration will be updated accordingly. Cheers, Marc ___ lilypond-user mailing list lilypond-user@gnu.org https://lists.gnu.org/mailman/listinfo/lilypond-user ___ lilypond-user mailing list lilypond-user@gnu.org https://lists.gnu.org/mailman/listinfo/lilypond-user ___ lilypond-user mailing list lilypond-user@gnu.org https://lists.gnu.org/mailman/listinfo/lilypond-user
Re: calculation of the total duration of a score
Hi Marc, nice to hear, that you have a workflow. For the record I noted two things in the file. (And if there's some spare time, I might further work on it.) 1. The durations are tagged by score and stored in an a-list, so you can first collect durations in multiple scores and display them later with a score-unique tag. 2. There are acknowledgers for volta-brackets and barlines. It should be possible to use that information for acknowledging volta repeats and ajusting the duration accordingly. Those grobs are created by engravers, but if one creates the barlines *not* with \repeat volta, but with \bar ".|:", it should be acknowledged too. Cheers Jan-Peter Am 15.09.2016 um 08:10 schrieb Marc Hohl: Am 14.09.2016 um 14:24 schrieb Marc Hohl: [...] I thought of writing the duration to a external file to be read from within the markup call in the score to be printed, but did not follow this route any further yet. Update: a simple test file shows that this works. I can - compute the total duration of a file designed for midi output only by means of your engraver - write the duration to a file and - read that string while processing the "print-only" file. Merging that stuff together with some Makefile should be straightforward, I hope ;-) And it has the advantage that some MIDI-related changes do not appear in the printed score, but the duration will be updated accordingly. Cheers, Marc ___ lilypond-user mailing list lilypond-user@gnu.org https://lists.gnu.org/mailman/listinfo/lilypond-user \version "2.19.47" % format seconds as min:sec #(define-public (format-time dur) (let* ((minutes (floor (/ dur 60))) (seconds (round (- dur (* 60 minutes)) ) )) (format "~2'0d:~2'0d" (inexact->exact minutes) (inexact->exact seconds)) )) % complete duration of scores - set after finalizing duration = #'() % markup command to display complete time #(define-markup-command (duration layout props scoretag)(symbol?) (let* ((gauge-stencil (interpret-markup layout props "00:00")) (x-ext (ly:stencil-extent gauge-stencil X)) (y-ext (ly:stencil-extent gauge-stencil Y))) (ly:make-stencil `(delay-stencil-evaluation ,(delay (ly:stencil-expr (interpret-markup layout props (format-time (ly:assoc-get scoretag duration 0 #t))) ))) x-ext y-ext) )) calcDuration = #(define-scheme-function (tag)(symbol?) (define (calc-duration context) (let ((dur 0) ; duration in seconds (start (ly:make-moment 0)) ; last calc-time the duration was calculated ) ; function to calculate duration in seconds since last calc-time (define (calc-dur) (let ((diff (ly:moment-sub (ly:context-now context) start) ) ; moment since last calc-time (tempo (ly:context-property context 'tempoWholesPerMinute (ly:make-moment 60/4)) )) ; current tempo (set! start (ly:context-now context)) ; set calc-time (set! dur ; add 60*(diff/tempo) to duration ; if tempo is 120 BPM and 120/4 elapsed, ; we have 60 * 120/4 * 4/120 = 60 seconds (+ dur (* 60 (exact->inexact (ly:moment-main (ly:moment-div diff tempo))) ))) dur )) (make-engraver (acknowledgers ((volta-bracket-interface engraver grob source-engraver) (ly:message "saw volta bracket @ ~A" (ly:context-now context))) ((bar-line-interface engraver grob source-engraver) (let ((bar (ly:context-property context 'whichBar))) (if (not (equal? "|" bar)) (ly:message "bar ~A" bar)) )) ) (listeners ((tempo-change-event engraver event) (calc-dur) ; calculate duration on every tempo-change-event (new tempo will be set after we listened to the event) (ly:message "duration: ~A" (format-time dur)) ) ) ; listeners ((finalize trans) (calc-dur) ; last calculation of duration (set! duration (assoc-set! duration tag dur)) ; set global duration (for the markup command) (ly:message "duration: ~A final" (format-time dur)) ) ))) #{ \with { \consists #calc-duration } #} ) % example % TODO repeats are not included !! meta = { \tempo 4=90 s1*4 | \tempo 4=120 \repeat volta 3 { s1*4 } \alternative { { s1 } { s1*2 } { \tempo 4=100 s1 } } s1*4 } \score { \new Staff << \meta \repeat unfold 32 \relative c'' { bes4 a c b } >> \layout { \context { \Score \calcDuration scoreBACH } } \midi { } } \markup { \box \duration #'scoreBACH } ___ lilypond-user mailing list lilypond-user@gnu.org https://li
Re: calculation of the total duration of a score
Am 14.09.2016 um 14:24 schrieb Marc Hohl: [...] I thought of writing the duration to a external file to be read from within the markup call in the score to be printed, but did not follow this route any further yet. Update: a simple test file shows that this works. I can - compute the total duration of a file designed for midi output only by means of your engraver - write the duration to a file and - read that string while processing the "print-only" file. Merging that stuff together with some Makefile should be straightforward, I hope ;-) And it has the advantage that some MIDI-related changes do not appear in the printed score, but the duration will be updated accordingly. Cheers, Marc ___ lilypond-user mailing list lilypond-user@gnu.org https://lists.gnu.org/mailman/listinfo/lilypond-user
Re: calculation of the total duration of a score
Hi Jan-Peter, Am 14.09.2016 um 13:18 schrieb Jan-Peter Voigt: Hi Marc, I am busy today, so I will look into your example later. No problem! In short: Repeats are not taken into account right now. Yes. Therefore I used the \unfoldRepeats/\midi-combination, but the duration computed in one \score is not visible in the other one. I thought of writing the duration to a external file to be read from within the markup call in the score to be printed, but did not follow this route any further yet. I thought of a factor, that is set, when a repeat starts. But I don't know yet, how to deal with alternatives and repeats with more than two loops. Probably the engraver might look for barlines. Still it needs to recognize alternatives with its possible repetitions (e.g. 1-3). I don't know whether it is doable to implement the stuff that \unfoldRepeats does within the engraver. Detecting the beginning and the end of a repeated section should not be that hard, but the bookkeeping is probably tricky in terms of alternatives, as you pointed out. We'll crack that nut :-) Sounds probising, thank you very much! Marc Jan-Peter Am 14. September 2016 12:03:43 MESZ, schrieb Marc Hohl : Hi Jan-Peter, ok, so I played around with your engraver. It does exactly what it needs to do, but somehow I am not able to get everything to work. I'll try to explain: Let's say that I have some music: music = { ...stuff... \repeat volta 2 { ... more stuff ... } } and want it displayed nicely: \score { \new Staff { \music } } If I include your engraver here, the calculated time is wrong, because the repeats are not taken into account. So I go for the midi-block \score { \new Staff { \unfoldRepeats \music } \midi { \context { \Score \consists ...your code here ... } } } But it looks like the duration variable is visible within its score only, see the attached compilable example. How can I obtain the correct duration in combination with a compact score? Thanks in advance, Marc Am 13.09.2016 um 10:24 schrieb Jan-Peter Voigt: Hi Marc, you already received some solutions, but I stumbled across this thread and just want to quickly show my engraver - perhaps it also helps. It doesn't read repeats right now, but this may be solved with \unfoldRepeats and the engraver placed in the midi-block. The duration-markup-command relies on a global variable and a delayed stencil - this will not work with multiple scores, as only the duration of the last score will be displayed. But that is manageable, if needed. HTH Jan-Peter Am 05.09.2016 um 11:01 schrieb Marc Hohl: Hi list, I have a couple of songs in my latest theatre project. It would be nice to have something like "duration: 3'22''" at the end of each song. Doing this by hand is straightforward:I generate a midi file that sounds accurate and let it play by timidity – but IMHO it would be less tedious and less error-prone if lilypond were capable of doing these calculations itself. Tempo indications and meter changes have to be taken into account, so this is probably doable with a special engraver only IIUC. Has someone else already done something like this? I have no experience in writing scheme engravers, so any hint would be highly appreciated. Thanks in advance, Marc lilypond-user mailing list lilypond-user@gnu.org https://lists.gnu.org/mailman/listinfo/lilypond-user lilypond-user mailing list lilypond-user@gnu.org https://lists.gnu.org/mailman/listinfo/lilypond-user lilypond-user mailing list lilypond-user@gnu.org https://lists.gnu.org/mailman/listinfo/lilypond-user -- Diese Nachricht wurde von meinem Android-Mobiltelefon mit K-9 Mail gesendet. ___ lilypond-user mailing list lilypond-user@gnu.org https://lists.gnu.org/mailman/listinfo/lilypond-user
Re: calculation of the total duration of a score
Hi Marc, I am busy today, so I will look into your example later. In short: Repeats are not taken into account right now. I thought of a factor, that is set, when a repeat starts. But I don't know yet, how to deal with alternatives and repeats with more than two loops. Probably the engraver might look for barlines. Still it needs to recognize alternatives with its possible repetitions (e.g. 1-3). We'll crack that nut :-) Jan-Peter Am 14. September 2016 12:03:43 MESZ, schrieb Marc Hohl : >Hi Jan-Peter, > >ok, so I played around with your engraver. It does exactly what it >needs >to do, but somehow I am not able to get everything to work. > >I'll try to explain: > >Let's say that I have some music: > >music = { > ...stuff... > \repeat volta 2 { ... more stuff ... } >} > >and want it displayed nicely: > >\score { > \new Staff { \music } >} > >If I include your engraver here, the calculated time is wrong, because >the repeats are not taken into account. > >So I go for the midi-block > >\score { > \new Staff { \unfoldRepeats \music } > \midi { > \context { > \Score > \consists ...your code here ... > } >} >} > > >But it looks like the duration variable is visible within its score >only, see the attached compilable example. > >How can I obtain the correct duration in combination with a compact >score? > >Thanks in advance, > >Marc >Am 13.09.2016 um 10:24 schrieb Jan-Peter Voigt: >> Hi Marc, >> >> you already received some solutions, but I stumbled across this >thread >> and just want to quickly show my engraver - perhaps it also helps. >> >> It doesn't read repeats right now, but this may be solved with >> \unfoldRepeats and the engraver placed in the midi-block. >> The duration-markup-command relies on a global variable and a delayed >> stencil - this will not work with multiple scores, as only the >duration >> of the last score will be displayed. But that is manageable, if >needed. >> >> HTH >> Jan-Peter >> >> Am 05.09.2016 um 11:01 schrieb Marc Hohl: >>> Hi list, >>> >>> I have a couple of songs in my latest theatre project. It would be >nice >>> to have something like "duration: 3'22''" at the end of each song. >>> >>> Doing this by hand is straightforward:I generate a midi file that >sounds >>> accurate and let it play by timidity – but IMHO it would be less >tedious >>> and less error-prone if lilypond were capable of doing these >>> calculations itself. >>> >>> Tempo indications and meter changes have to be taken into account, >so >>> this is probably doable with a special engraver only IIUC. >>> >>> Has someone else already done something like this? I have no >experience >>> in writing scheme engravers, so any hint would be highly >appreciated. >>> >>> Thanks in advance, >>> >>> Marc >>> >>> ___ >>> lilypond-user mailing list >>> lilypond-user@gnu.org >>> https://lists.gnu.org/mailman/listinfo/lilypond-user >> >> >> >> ___ >> lilypond-user mailing list >> lilypond-user@gnu.org >> https://lists.gnu.org/mailman/listinfo/lilypond-user >> > > > > > >___ >lilypond-user mailing list >lilypond-user@gnu.org >https://lists.gnu.org/mailman/listinfo/lilypond-user -- Diese Nachricht wurde von meinem Android-Mobiltelefon mit K-9 Mail gesendet.___ lilypond-user mailing list lilypond-user@gnu.org https://lists.gnu.org/mailman/listinfo/lilypond-user
Re: calculation of the total duration of a score
Hi Jan-Peter, ok, so I played around with your engraver. It does exactly what it needs to do, but somehow I am not able to get everything to work. I'll try to explain: Let's say that I have some music: music = { ...stuff... \repeat volta 2 { ... more stuff ... } } and want it displayed nicely: \score { \new Staff { \music } } If I include your engraver here, the calculated time is wrong, because the repeats are not taken into account. So I go for the midi-block \score { \new Staff { \unfoldRepeats \music } \midi { \context { \Score \consists ...your code here ... } } } But it looks like the duration variable is visible within its score only, see the attached compilable example. How can I obtain the correct duration in combination with a compact score? Thanks in advance, Marc Am 13.09.2016 um 10:24 schrieb Jan-Peter Voigt: Hi Marc, you already received some solutions, but I stumbled across this thread and just want to quickly show my engraver - perhaps it also helps. It doesn't read repeats right now, but this may be solved with \unfoldRepeats and the engraver placed in the midi-block. The duration-markup-command relies on a global variable and a delayed stencil - this will not work with multiple scores, as only the duration of the last score will be displayed. But that is manageable, if needed. HTH Jan-Peter Am 05.09.2016 um 11:01 schrieb Marc Hohl: Hi list, I have a couple of songs in my latest theatre project. It would be nice to have something like "duration: 3'22''" at the end of each song. Doing this by hand is straightforward:I generate a midi file that sounds accurate and let it play by timidity – but IMHO it would be less tedious and less error-prone if lilypond were capable of doing these calculations itself. Tempo indications and meter changes have to be taken into account, so this is probably doable with a special engraver only IIUC. Has someone else already done something like this? I have no experience in writing scheme engravers, so any hint would be highly appreciated. Thanks in advance, Marc ___ lilypond-user mailing list lilypond-user@gnu.org https://lists.gnu.org/mailman/listinfo/lilypond-user ___ lilypond-user mailing list lilypond-user@gnu.org https://lists.gnu.org/mailman/listinfo/lilypond-user \version "2.19.47" %%% engraver definitions % format seconds as min:sec #(define-public (format-time dur) (let* ((minutes (floor (/ dur 60))) (seconds (round (- dur (* 60 minutes)) ) )) (format "~2'0d:~2'0d" (inexact->exact minutes) (inexact->exact seconds)) )) % complete duration of score - set after finalizing duration = 0 % markup command to display complete time #(define-markup-command (duration layout props)() (let* ((gauge-stencil (interpret-markup layout props "00:00")) (x-ext (ly:stencil-extent gauge-stencil X)) (y-ext (ly:stencil-extent gauge-stencil Y))) (ly:make-stencil `(delay-stencil-evaluation ,(delay (ly:stencil-expr (interpret-markup layout props (format-time duration)) ))) x-ext y-ext) )) calc-duration-engraver = #(lambda (context) (let ((dur 0) ; duration in seconds (start (ly:make-moment 0)) ; last calc-time the duration was calculated ) ; function to calculate duration in seconds since last calc-time (define (calc-dur) (let ((diff (ly:moment-sub (ly:context-now context) start) ) ; moment since last calc-time (tempo (ly:context-property context 'tempoWholesPerMinute (ly:make-moment 60/4)) )) ; current tempo (set! start (ly:context-now context)) ; set calc-time (set! dur ; add 60*(diff/tempo) to duration ; if tempo is 120 BPM and 120/4 elapsed, ; we have 60 * 120/4 * 4/120 = 60 seconds (+ dur (* 60 (exact->inexact (ly:moment-main (ly:moment-div diff tempo))) ))) dur )) (make-engraver (listeners ((tempo-change-event engraver event) (calc-dur) ; calculate duration on every tempo-change-event (new tempo will be set after we listened to the event) (ly:message "duration: ~A" (format-time dur)) ) ) ; listeners ((finalize trans) (calc-dur) ; last calculation of duration (set! duration dur) ; set global duration (for the markup command) (ly:message "duration: ~A final" (format-time dur)) ) ))) %%% actual music example music = { \tempo 4 = 60 c'4 c' c' c' \repeat volta 2 { d'2 d' | e' e' } \tempo 4 = 90 c'4 c' c' c' } %%% scores \score { \new Staff { \unfoldRepeats \music } \midi {
Re: calculation of the total duration of a score
Hi Jan-Peter, seems to do exactly what I need, so thanks for sharing ... I'll give it a try tomorrow, when I have some time to work on my project again. Cheers, Marc Am 13.09.2016 um 10:24 schrieb Jan-Peter Voigt: Hi Marc, you already received some solutions, but I stumbled across this thread and just want to quickly show my engraver - perhaps it also helps. It doesn't read repeats right now, but this may be solved with \unfoldRepeats and the engraver placed in the midi-block. The duration-markup-command relies on a global variable and a delayed stencil - this will not work with multiple scores, as only the duration of the last score will be displayed. But that is manageable, if needed. HTH Jan-Peter Am 05.09.2016 um 11:01 schrieb Marc Hohl: Hi list, I have a couple of songs in my latest theatre project. It would be nice to have something like "duration: 3'22''" at the end of each song. Doing this by hand is straightforward:I generate a midi file that sounds accurate and let it play by timidity – but IMHO it would be less tedious and less error-prone if lilypond were capable of doing these calculations itself. Tempo indications and meter changes have to be taken into account, so this is probably doable with a special engraver only IIUC. Has someone else already done something like this? I have no experience in writing scheme engravers, so any hint would be highly appreciated. Thanks in advance, Marc ___ lilypond-user mailing list lilypond-user@gnu.org https://lists.gnu.org/mailman/listinfo/lilypond-user ___ lilypond-user mailing list lilypond-user@gnu.org https://lists.gnu.org/mailman/listinfo/lilypond-user ___ lilypond-user mailing list lilypond-user@gnu.org https://lists.gnu.org/mailman/listinfo/lilypond-user
Re: calculation of the total duration of a score
Hi Marc, you already received some solutions, but I stumbled across this thread and just want to quickly show my engraver - perhaps it also helps. It doesn't read repeats right now, but this may be solved with \unfoldRepeats and the engraver placed in the midi-block. The duration-markup-command relies on a global variable and a delayed stencil - this will not work with multiple scores, as only the duration of the last score will be displayed. But that is manageable, if needed. HTH Jan-Peter Am 05.09.2016 um 11:01 schrieb Marc Hohl: Hi list, I have a couple of songs in my latest theatre project. It would be nice to have something like "duration: 3'22''" at the end of each song. Doing this by hand is straightforward:I generate a midi file that sounds accurate and let it play by timidity – but IMHO it would be less tedious and less error-prone if lilypond were capable of doing these calculations itself. Tempo indications and meter changes have to be taken into account, so this is probably doable with a special engraver only IIUC. Has someone else already done something like this? I have no experience in writing scheme engravers, so any hint would be highly appreciated. Thanks in advance, Marc ___ lilypond-user mailing list lilypond-user@gnu.org https://lists.gnu.org/mailman/listinfo/lilypond-user \version "2.19.47" % format seconds as min:sec #(define-public (format-time dur) (let* ((minutes (floor (/ dur 60))) (seconds (round (- dur (* 60 minutes)) ) )) (format "~2'0d:~2'0d" (inexact->exact minutes) (inexact->exact seconds)) )) % complete duration of score - set after finalizing duration = 0 % markup command to display complete time #(define-markup-command (duration layout props)() (let* ((gauge-stencil (interpret-markup layout props "00:00")) (x-ext (ly:stencil-extent gauge-stencil X)) (y-ext (ly:stencil-extent gauge-stencil Y))) (ly:make-stencil `(delay-stencil-evaluation ,(delay (ly:stencil-expr (interpret-markup layout props (format-time duration)) ))) x-ext y-ext) )) % consist an engraver to collect time \layout { \context { \Score \consists #(lambda (context) (let ((dur 0) ; duration in seconds (start (ly:make-moment 0)) ; last calc-time the duration was calculated ) ; function to calculate duration in seconds since last calc-time (define (calc-dur) (let ((diff (ly:moment-sub (ly:context-now context) start) ) ; moment since last calc-time (tempo (ly:context-property context 'tempoWholesPerMinute (ly:make-moment 60/4)) )) ; current tempo (set! start (ly:context-now context)) ; set calc-time (set! dur ; add 60*(diff/tempo) to duration ; if tempo is 120 BPM and 120/4 elapsed, ; we have 60 * 120/4 * 4/120 = 60 seconds (+ dur (* 60 (exact->inexact (ly:moment-main (ly:moment-div diff tempo))) ))) dur )) (make-engraver (listeners ((tempo-change-event engraver event) (calc-dur) ; calculate duration on every tempo-change-event (new tempo will be set after we listened to the event) (ly:message "duration: ~A" (format-time dur)) ) ) ; listeners ((finalize trans) (calc-dur) ; last calculation of duration (set! duration dur) ; set global duration (for the markup command) (ly:message "duration: ~A final" (format-time dur)) ) ))) } } % example % TODO repeats are not included !! meta = { \tempo 4=90 s1*4 | \tempo 4=120 \repeat volta 2 { s1*4 } } \score { \new Staff << \meta \repeat unfold 8 \relative c'' { bes4 a c b } >> \layout { } \midi { } } \markup { \box \duration } ___ lilypond-user mailing list lilypond-user@gnu.org https://lists.gnu.org/mailman/listinfo/lilypond-user
Re: calculation of the total duration of a score
Hi Harm, On 09/10/2016 02:48 PM, Thomas Morley wrote: many thanks for looking into it and adding a diff, much easier to compare the versions. :) You're welcome! Thanks for writing the code for me to test out. :) Also for the insufficient (if duration-marks which was kept from an earlier version. Ofcourse your correction is the way to go. Well, I realized afterwards that (pair? lst) is probably the usual scheme way rather than (not (null? lst)) although the latter arguably makes the intention clearer for readers who don't know scheme as well. Well, at first it's a matter of code-hygiene. Those variables were filled by the engraver doing it's work, why not clear them? Additional I fear bleeding over, if the same engraver is used while compiling multiple scores/files. Or if he is put into multiple contexts of the same score. Not entirely sure about this, but better be a paranoiac ;) Indeed! That sounds good to me. I incorporated some of your approach into the patch I did for lilypond-html-live-score, in case anyone is curious: https://gitlab.com/sigmate/lilypond-html-live-score/commit/8d1fe4d9f8d39a2849bf737136ee2cca419b9fb6 Cheers, -Paul ___ lilypond-user mailing list lilypond-user@gnu.org https://lists.gnu.org/mailman/listinfo/lilypond-user
Re: calculation of the total duration of a score
Am 11.09.2016 um 12:16 schrieb David Kastrup: [...] Would this be somehow possible? I probably lack the imagination for seeing your problem. I need the total duration in terms of minutes and seconds, taking all possible tempo changes etc. into account. ___ lilypond-user mailing list lilypond-user@gnu.org https://lists.gnu.org/mailman/listinfo/lilypond-user
Re: calculation of the total duration of a score
Marc Hohl writes: > Hi Harm, hi Paul, > > thanks a lot for your code/improvements/discussion to this subject! > > Definitively worth to be put in the LSR! > > Some remarks from me as a real-life user: > > the engraver does not expand repeats, so if there ist some \repeat > volta ... construct around, the total duration is not only wrong > (which is clear from the way the engraver works) but somewhat strange. > > So my humble question to the scheme wizards out there: is it possible > to store the total duration in a variable/property to be read > afterwards? > > I think of something like > > music = { ... } > > \score { >\new Staff { > \music > \mark \markup "Duration: > #(get-total-duration #{ \unfoldRepeats \music #})" > \bar "|." >} > } > > (just pseudo-code, of course) > > Would this be somehow possible? I probably lack the imagination for seeing your problem. music = \fixed c' { c1 \repeat volta 4 { g1 } \alternative { { c1 g1 } { r1 } } } \score { \new Staff { \music \mark \markup #(format "Duration: ~a" (ly:moment-main (ly:music-length #{ \unfoldRepeats \music #}))) \bar "|." } } -- David Kastrup ___ lilypond-user mailing list lilypond-user@gnu.org https://lists.gnu.org/mailman/listinfo/lilypond-user
Re: calculation of the total duration of a score
Hi Harm, hi Paul, thanks a lot for your code/improvements/discussion to this subject! Definitively worth to be put in the LSR! Some remarks from me as a real-life user: the engraver does not expand repeats, so if there ist some \repeat volta ... construct around, the total duration is not only wrong (which is clear from the way the engraver works) but somewhat strange. So my humble question to the scheme wizards out there: is it possible to store the total duration in a variable/property to be read afterwards? I think of something like music = { ... } \score { \new Staff { \music \mark \markup "Duration: #(get-total-duration #{ \unfoldRepeats \music #})" \bar "|." } } (just pseudo-code, of course) Would this be somehow possible? Thanks, Marc Am 10.09.2016 um 20:48 schrieb Thomas Morley: Hi Paul, many thanks for looking into it and adding a diff, much easier to compare the versions. :) 2016-09-10 20:08 GMT+02:00 Paul : On 09/09/2016 05:27 PM, Thomas Morley wrote: Attached you'll find a different version. I noticed that text-only tempos (\tempo "Andante") caused problems. See attached revision which fixes this. Good catch, I overlooked this. Also for the insufficient (if duration-marks which was kept from an earlier version. Ofcourse your correction is the way to go. It also has improved code for detecting a missing initial tempo (to then supply the default one). See attached diff file for changes. You asked: 2016-09-10 16:40 GMT+02:00 Paul : Also, I wonder, where you clear out these variables at the end of the finalize stage: (set! evts '()) (set! last-evt #f) (set! tempo-change-evts '()) (set! duration-marks '()) Of course, it doesn't hurt, but is it necessary? If so I need to do that in my code. Well, at first it's a matter of code-hygiene. Those variables were filled by the engraver doing it's work, why not clear them? Additional I fear bleeding over, if the same engraver is used while compiling multiple scores/files. Or if he is put into multiple contexts of the same score. Not entirely sure about this, but better be a paranoiac ;) Thanks for your thorough review. I think I'll follow your suggestion and put it in the LSR. Best, Harm ___ lilypond-user mailing list lilypond-user@gnu.org https://lists.gnu.org/mailman/listinfo/lilypond-user ___ lilypond-user mailing list lilypond-user@gnu.org https://lists.gnu.org/mailman/listinfo/lilypond-user
Re: calculation of the total duration of a score
Hi Paul, many thanks for looking into it and adding a diff, much easier to compare the versions. :) 2016-09-10 20:08 GMT+02:00 Paul : > On 09/09/2016 05:27 PM, Thomas Morley wrote: > >> Attached you'll find a different version. > > > I noticed that text-only tempos (\tempo "Andante") caused problems. See > attached revision which fixes this. Good catch, I overlooked this. Also for the insufficient (if duration-marks which was kept from an earlier version. Ofcourse your correction is the way to go. > It also has improved code for detecting > a missing initial tempo (to then supply the default one). See attached diff > file for changes. You asked: 2016-09-10 16:40 GMT+02:00 Paul : > Also, I wonder, where you clear out these variables at the end of the > finalize stage: > > (set! evts '()) > (set! last-evt #f) > (set! tempo-change-evts '()) > (set! duration-marks '()) > > Of course, it doesn't hurt, but is it necessary? If so I need to do that in > my code. Well, at first it's a matter of code-hygiene. Those variables were filled by the engraver doing it's work, why not clear them? Additional I fear bleeding over, if the same engraver is used while compiling multiple scores/files. Or if he is put into multiple contexts of the same score. Not entirely sure about this, but better be a paranoiac ;) Thanks for your thorough review. I think I'll follow your suggestion and put it in the LSR. Best, Harm ___ lilypond-user mailing list lilypond-user@gnu.org https://lists.gnu.org/mailman/listinfo/lilypond-user
Re: calculation of the total duration of a score
On 09/09/2016 05:27 PM, Thomas Morley wrote: Attached you'll find a different version. I noticed that text-only tempos (\tempo "Andante") caused problems. See attached revision which fixes this. It also has improved code for detecting a missing initial tempo (to then supply the default one). See attached diff file for changes. Cheers, -Paul \version "2.19.47" printScoreDuration = { \once \override Score.RehearsalMark.direction = #DOWN \once \override Score.RehearsalMark.self-alignment-X = #RIGHT \mark "print-score-duration" } formatScoreDuration = #(define-scheme-function (dur)(exact?) "Returns the given duration as a formated markup containing minutes and seconds." (let* ((minutes (floor dur)) ;; Is using floor correct? (seconds (floor (* (- dur minutes ) 60))) (duration-string (format #f "Duration: ~a:~2,,,'0@a" minutes seconds))) #{ \markup \rounded-box \fontsize #-3 #duration-string #})) #(define (get-seconds lst rl) "Takes a list of kind '((# 1/15) (# 1/30) (# 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) (tempo-change-evts '()) (duration-marks '())) (make-engraver (listeners ((rhythmic-event engraver event) (set! last-evt (ly:event-property event 'length)) (set! evts (cons (ly:context-current-moment context) evts))) ((tempo-change-event engraver event) (let ((tempo-unit (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' ;; text-only tempo changes like \tempo "Andante" have no tempo-unit (if (and tempo-unit metronome-count) (set! tempo-change-evts (cons (cons (ly:context-current-moment context) ;; Hmm, ugly code... (/ (string->number (ly:duration->string tempo-unit)) metronome-count)) tempo-change-evts)) (acknowledgers ((mark-interface engraver grob source-engraver) (let ((mark-text (ly:grob-property grob 'text))) (if (and (string? mark-text) (string=? mark-text "print-score-duration")) (set! duration-marks (cons grob duration-marks)) ((finalize translator) (if (not (null? duration-marks)) (let* ((moment-zero (ly:make-moment 0)) ; add default tempo at moment zero, if one does not already exist (tempo-changes (if (or (null? tempo-change-evts) (not (equal? moment-zero (car (last tempo-change-evts) ;; 1/15 is default tempo, could this be accessed somewhere? (append tempo-change-evts (list (cons moment-zero 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))) (for-each (lambda (g) (ly:grob-set-property! g 'text (formatScoreDuration final-duration))) duration-marks) (set! evts '()) (set! last-evt #f) (set! tempo-change-evts '()) (set! duration-marks '( \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~ | \tuplet 3/2 { c'2 2 2 } \tempo "Andante" c'2 c'2 } voiceII = { \partial 4 cis'4 \printScoreDuration \repeat unfold 17 cis'1 %% fiddling with two simultaneous RehearsalMarks... cis'1*31/32 \printScoreDurati
Re: calculation of the total duration of a score
On 09/09/2016 05:27 PM, Thomas Morley wrote: Attached you'll find a different version. It avoids creating all those grobs and killing most of them in the end. Instead a preexisting RehearsalMark (or more of them) is taken and 'text is reset. Some other changes for better usability. Hi Harm, Nice work! I'd say this is a good candidate for the LSR, particularly because it demonstrates listeners, acknowledgers, and finalize in one engraver. One little thing I noticed is that (if duration-marks should be something like (if (not (null? duration-marks)) Because: \version "2.19.42" #(define empty-list '()) #(display (if empty-list #t #f)) % --> #t #(display (if (not (null? empty-list)) #t #f)) % --> #f Also, I wonder, where you clear out these variables at the end of the finalize stage: (set! evts '()) (set! last-evt #f) (set! tempo-change-evts '()) (set! duration-marks '()) Of course, it doesn't hurt, but is it necessary? If so I need to do that in my code. -Paul ___ lilypond-user mailing list lilypond-user@gnu.org https://lists.gnu.org/mailman/listinfo/lilypond-user
Re: calculation of the total duration of a score
2016-09-08 14:21 GMT+02:00 Marc Hohl : > Hi Harm, > > thanks a lot for your engraver, works out of the box! > > Best regards, > > Marc Hi Marc, you're welcome. Attached you'll find a different version. It avoids creating all those grobs and killing most of them in the end. Instead a preexisting RehearsalMark (or more of them) is taken and 'text is reset. Some other changes for better usability. HTH, Harm \version "2.19.47" printScoreDuration = { \once \override Score.RehearsalMark.direction = #DOWN \once \override Score.RehearsalMark.self-alignment-X = #RIGHT \mark "print-score-duration" } formatScoreDuration = #(define-scheme-function (dur)(exact?) "Returns the given duration as a formated markup containing minutes and seconds." (let* ((minutes (floor dur)) ;; Is using floor correct? (seconds (floor (* (- dur minutes ) 60))) (duration-string (format #f "Duration: ~a:~2,,,'0@a" minutes seconds))) #{ \markup \rounded-box \fontsize #-3 #duration-string #})) #(define (get-seconds lst rl) "Takes a list of kind '((# 1/15) (# 1/30) (# 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) (tempo-change-evts '()) (duration-marks '())) (make-engraver (listeners ((rhythmic-event engraver event) (set! last-evt (ly:event-property event 'length)) (set! evts (cons (ly:context-current-moment context) evts))) ((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) (acknowledgers ((mark-interface engraver grob source-engraver) (let ((mark-text (ly:grob-property grob 'text))) (if (and (string? mark-text) (string=? mark-text "print-score-duration")) (set! duration-marks (cons grob duration-marks)) ((finalize translator) (if duration-marks (let* (;; default tempo, could this be grapped somewhere? (default-tempo-setting (cons (ly:make-moment 0) 1/15)) ; add default tempo, if not introduced at score-begin (tempo-changes (if (or (null? tempo-change-evts) (not (equal? (last tempo-change-evts) default-tempo-setting))) (append tempo-change-evts (list default-tempo-setting)) 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))) (for-each (lambda (g) (ly:grob-set-property! g 'text (formatScoreDuration final-duration))) duration-marks) (set! evts '()) (set! last-evt #f) (set! tempo-change-evts '()) (set! duration-marks '( \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~ | \tuplet 3/2 { c'2 2 2 } } voiceII = { \partial 4 cis'4 \printScoreDuration \repeat unfold 17 cis'1 %% fiddling with two simultaneous RehearsalMarks... cis'1*31/32 \printScoreDuration s1*1/32 \mark \default } \score { << \voiceI \voiceII >> \layout { } \midi {} } ___ lilypond-use
Re: calculation of the total duration of a score
Hi Paul, Am 08.09.2016 um 19:28 schrieb Paul: Hi Marc, Well, looks like I sent the file too soon before testing it for this use case. It looks like the html-live-score is using different units of time somehow? I haven't had a chance to look into it or figure out the difference. Ok, no problem, thanks for clarification! Marc ___ lilypond-user mailing list lilypond-user@gnu.org https://lists.gnu.org/mailman/listinfo/lilypond-user
Re: calculation of the total duration of a score
Hi Marc, Well, looks like I sent the file too soon before testing it for this use case. It looks like the html-live-score is using different units of time somehow? I haven't had a chance to look into it or figure out the difference. Anyway, the attached file will report the correct total duration, thanks to some help from looking at Harm's code. (But I think this version will probably not work with the code for html-live-score...) Anyway, you're probably better off using Harm's code since it was written for calculating the total duration. Cheers, -Paul On 09/08/2016 06:10 AM, Marc Hohl wrote: Hi Paul, thanks for the file. It lookes quite promising, but a quick test shows some strange numbers I do not understand: test.ly --- \version "2.19.47" \include "grob-meta-data-engraver.ily" \score { \new Staff { \tempo 4 = 60 \repeat unfold 60 c'4 } \layout { } \midi { } } Processing this file yields to: GNU LilyPond 2.19.48 »test.ly« wird verarbeitet Analysieren... Interpretation der Musik...[8][16][16]3.75 Vorverarbeitung der grafischen Elemente... Interpretation der Musik... MIDI-Ausgabe nach »test.midi«... Ideale Seitenanzahl wird gefunden... Musik wird auf eine Seite angepasst... Systeme erstellen... Layout nach »/tmp/lilypond-pgKSZe« ausgeben... Konvertierung nach »test.pdf«... Löschen von »/tmp/lilypond-pgKSZe«... Kompilation erfolgreich beendet marc@olivia ~/engraver$ timidity test.midi Playing test.midi MIDI file: test.midi Format: 1 Tracks: 2 Divisions: 384 Sequence: Text: creator: Text: GNU LilyPond 2.19.48 Playing time: ~64 seconds Notes cut: 0 Notes lost totally: 0 It shows a duration of 3.75 in line 4. A timidity call plays the file in about 64 seconds (even strange, 60 notes with tempo 4 = 60 should have a total duration of 60 seconds). What does 3.75 mean? Marc Am 08.09.2016 um 05:39 schrieb Paul: 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 ___ lilypond-user mailing list lilypond-user@gnu.org https://lists.gnu.org/mailman/listinfo/lilypond-user ___ lilypond-user mailing list lilypond-user@gnu.org https://lists.gnu.org/mailman/listinfo/lilypond-user \version "2.19.42" #(define Grob_meta_data_engraver (lambda (context) (define (get-tempo-change metronome-mark-grob) "Returns a pair (moment-fraction . new-tempo-rate) or #f." (let* ((grob-cause (ly:grob-property metronome-mark-grob 'cause)) (metronome-count (ly:event-property grob-cause 'metronome-count)) (tempo-unit (ly:event-property grob-cause 'tempo-unit)) (moment (grob::when metronome-mark-grob))) (if (and metronome-count tempo-unit) (cons (ly:moment-main moment) ;; calculate the new tempo rate (/ (string->number (ly:duration->string tempo-unit)) metronome-count)) #f))) (define (recurse prev-moment prev-time prev-rate grobs tempo-changes) "Recursive function to calculate and set timing data for grobs. Calculates the actual timing of grobs, honoring tempo changes. Returns the total time for the score." (let* ((grob (car grobs)) (moment (ly:moment-main (grob::when grob))) (rate-change (if (pair? tempo-changes) (> moment (caar tempo-changes)) #f)) (rate (if rate-change (cdr (car tempo-changes)) prev-rate)) (time (if (= moment prev-moment) prev-time (+ prev-time (* rate (- moment prev-moment) (id-string (string-append (ly:format "class:ly grob ~a" (grob::name grob)) (ly:format ";data-moment:~a" (exact->inexact moment)) (ly:format ";data-measure:~a" (car (grob::rhythmic-location grob))) (ly:format ";data-real-time:~a" time) ))) ;; (display id-string)(newline) (ly:grob-set-property! grob 'id id-string) ;; r
Re: calculation of the total duration of a score
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 : 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 '((# 1/15) (# 1/30) (# 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,,,'0@a" 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
Re: calculation of the total duration of a score
Hi Paul, thanks for the file. It lookes quite promising, but a quick test shows some strange numbers I do not understand: test.ly --- \version "2.19.47" \include "grob-meta-data-engraver.ily" \score { \new Staff { \tempo 4 = 60 \repeat unfold 60 c'4 } \layout { } \midi { } } Processing this file yields to: GNU LilyPond 2.19.48 »test.ly« wird verarbeitet Analysieren... Interpretation der Musik...[8][16][16]3.75 Vorverarbeitung der grafischen Elemente... Interpretation der Musik... MIDI-Ausgabe nach »test.midi«... Ideale Seitenanzahl wird gefunden... Musik wird auf eine Seite angepasst... Systeme erstellen... Layout nach »/tmp/lilypond-pgKSZe« ausgeben... Konvertierung nach »test.pdf«... Löschen von »/tmp/lilypond-pgKSZe«... Kompilation erfolgreich beendet marc@olivia ~/engraver$ timidity test.midi Playing test.midi MIDI file: test.midi Format: 1 Tracks: 2 Divisions: 384 Sequence: Text: creator: Text: GNU LilyPond 2.19.48 Playing time: ~64 seconds Notes cut: 0 Notes lost totally: 0 It shows a duration of 3.75 in line 4. A timidity call plays the file in about 64 seconds (even strange, 60 notes with tempo 4 = 60 should have a total duration of 60 seconds). What does 3.75 mean? Marc Am 08.09.2016 um 05:39 schrieb Paul: 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 ___ lilypond-user mailing list lilypond-user@gnu.org https://lists.gnu.org/mailman/listinfo/lilypond-user ___ lilypond-user mailing list lilypond-user@gnu.org https://lists.gnu.org/mailman/listinfo/lilypond-user
Re: calculation of the total duration of a score
2016-09-08 5:39 GMT+02:00 Paul : > 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 '((# 1/15) (# 1/30) (# 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,,,'0@a" 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 { << \voi
Re: calculation of the total duration of a score
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 \version "2.19.42" #(define Grob_meta_data_engraver (lambda (context) (define (get-tempo-change metronome-mark-grob) "Returns a pair (moment-fraction . new-tempo-rate) or #f." (let* ((grob-cause (ly:grob-property metronome-mark-grob 'cause)) (metronome-count (ly:event-property grob-cause 'metronome-count)) (tempo-unit (ly:event-property grob-cause 'tempo-unit)) (moment (grob::when metronome-mark-grob))) (if (and metronome-count tempo-unit) (cons (ly:moment-main moment) ;; calculate the new tempo rate (/ 60 (* metronome-count (string->number (ly:duration->string tempo-unit) #f))) (define (recurse prev-moment prev-time prev-rate grob grobs tempo-changes) "Recursive function to calculate and set timing data for grobs. Calculates the actual timing of grobs, honoring tempo changes. Returns the total time for the score." (let* ((moment (ly:moment-main (grob::when grob))) (rate-change (if (pair? tempo-changes) (>= moment (caar tempo-changes)) #f)) (rate (if rate-change (cdr (car tempo-changes)) prev-rate)) (time (if (= moment prev-moment) prev-time (+ prev-time (* rate (- moment prev-moment) (id-string (string-append (ly:format "class:ly grob ~a" (grob::name grob)) (ly:format ";data-moment:~a" (exact->inexact moment)) (ly:format ";data-measure:~a" (car (grob::rhythmic-location grob))) (ly:format ";data-real-time:~a" time) ))) ;; (display id-string)(newline) (ly:grob-set-property! grob 'id id-string) ;; recurse or return total time if we are done (if (null? grobs) time (recurse moment time rate (car grobs) (cdr grobs) (if rate-change (cdr tempo-changes) tempo-changes) ;; an engraver with a closure (let ((grobs '()) (metronome-mark-grobs '()) (note-head-grobs '())) (make-engraver ;; acknowledgers collect grobs (acknowledgers ((grob-interface engraver grob source-engraver) (set! grobs (cons grob grobs))) ((metronome-mark-interface engraver grob source-engraver) (set! metronome-mark-grobs (cons grob metronome-mark-grobs))) ((note-head-interface engraver grob source-engraver) (set! note-head-grobs (cons grob note-head-grobs ;; finalize stage, calculate and store data on grobs ((finalize translator) (let* ((tempo-changes (filter pair? (map get-tempo-change metronome-mark-grobs))) (tempo-changes-sorted (sort-list! tempo-changes (lambda (a b) (< (car a) (car b) (grobs-sorted (sort-list! (filter grob::name grobs) (lambda (a b) (ly:momentinexact total-time)) ;; add additional data for MetronomeMark grobs (for-each (lambda (metronome-mark-grob) (let* ((grob-cause (ly:grob-property metronome-mark-grob 'cause)) (text-prop (ly:event-property grob-cause 'text)) (text-string (if (not (null? text-prop)) (ly:format ";data-text:~a" text-prop) "")) (id-string (ly:grob-property metronome-mark-grob 'id))) (ly:grob-set-property! metronome-mark-grob 'id (string-append id-string text-string metronome-mark-grobs) ;; add additional data for NoteHead grobs
Re: calculation of the total duration of a score
Am 05.09.2016 um 11:54 schrieb Federico Bruni: Il giorno lun 5 set 2016 alle 11:01, Marc Hohl ha scritto: [...] I have not tried, but maybe python-ly is able to give what you want? http://python-ly.readthedocs.io/en/latest/ly.music.html Thanks for the link. If I haven't overlooked an important detail, python-ly does not provide this function out-of-the-box. Moreover, a scheme-ish solution allows for direct generation of a markup to be placed at the final bar line. Marc ___ lilypond-user mailing list lilypond-user@gnu.org https://lists.gnu.org/mailman/listinfo/lilypond-user
Re: calculation of the total duration of a score
Il giorno lun 5 set 2016 alle 11:01, Marc Hohl ha scritto: I have a couple of songs in my latest theatre project. It would be nice to have something like "duration: 3'22''" at the end of each song. Doing this by hand is straightforward:I generate a midi file that sounds accurate and let it play by timidity – but IMHO it would be less tedious and less error-prone if lilypond were capable of doing these calculations itself. Tempo indications and meter changes have to be taken into account, so this is probably doable with a special engraver only IIUC. Has someone else already done something like this? I have no experience in writing scheme engravers, so any hint would be highly appreciated. I have not tried, but maybe python-ly is able to give what you want? http://python-ly.readthedocs.io/en/latest/ly.music.html ___ lilypond-user mailing list lilypond-user@gnu.org https://lists.gnu.org/mailman/listinfo/lilypond-user