Re: lilypond "preprocessor"?
On Tue, 2023-12-12 at 14:46 -0800, Stefano Antonelli wrote: > #(define (add-midi-to-score score) >(define (has-midi? score) > (any (lambda (x) (ly:output-def-lookup x 'is-midi)) > (ly:score-output-defs score))) >(if (has-midi? score) score > #{ \score { $score \midi {} } #})) > > toplevel-score-handler = > #(lambda (score) >(collect-scores-for-book (add-midi-to-score score))) It seems to me, that in order to make this work, two things need to happen: 1. The whole score collection has to be traversed and \midi counted This could be done via toplevel-score-handler and some global variable. 2. If there are no midi blocks, add one. This is tricker. Assuming there is a toplevel-of-everything-handler, it would be necessary to find a score (which one though?) and then add a midi block. I took a shot at the first one: #(define midi-count 0) #(define (has-midi? score) (any (lambda (x) (ly:output-def-lookup x 'is-midi #f)) (ly:score-output-defs score))) #(define (count-midi-in-score score) (if (has-midi? score) (set! midi-count (+ midi-count 1))) score) toplevel-score-handler = #(lambda (score) (collect-scores-for-book (count-midi-in-score score))) midi-count now contains the right number. What can I do with it?
Re: lilypond "preprocessor"?
On 2023-12-12 11:26, Werner LEMBERG wrote: One thing occurred to me. If the original \score did not have a \midi block, would it be possible to ask lilypond to produce midi output without modifying the input file? No, this is not currently possible, although it would be a nice addition. Stefano, please file an issue for this in our tracker! Sounds tricky to implement such a features that does what the user expects in all different situations, especially for files that contain several \score blocks and especially if the user already has made separate \score blocks for the typeset version and the MIDI version, in which case you don't want to add another MIDI version. /Mats
Re: lilypond "preprocessor"?
On Tue, 2023-12-12 at 12:40 -0800, Aaron Hill wrote: > Any better? > > > #(define (add-midi-to-score score) >(define (has-midi? score) > (any (lambda (x) (ly:output-def-lookup x 'is-midi)) > (ly:score-output-defs score))) >(if (has-midi? score) score > #{ \score { $score \midi {} } #})) > I was curious about few different test cases so I wrote a script to automate some tests. Test cases are described at the end of this email. In all cases there would ideally be only 1 midi file. Case 1: Found 0 midi files and 1 pdf files. Case 2: Found 1 midi files and 1 pdf files. Case 3: Found 0 midi files and 1 pdf files. Case 4: Found 1 midi files and 1 pdf files. Case 5: Found 0 midi files and 2 pdf files. Case 6: Found 1 midi files and 1 pdf files. With David's modification: (ly:output-def-lookup x 'is-midi #f) making the function look like this: #(define (add-midi-to-score score) (define (has-midi? score) (any (lambda (x) (ly:output-def-lookup x 'is-midi #f)) (ly:score-output-defs score))) (if (has-midi? score) score #{ \score { $score \midi {} } #})) Case 1: Found 1 midi files and 1 pdf files. Case 2: Found 2 midi files and 1 pdf files. Case 3: Found 2 midi files and 1 pdf files. Case 4: Found 3 midi files and 1 pdf files. Case 5: Found 0 midi files and 2 pdf files. Case 6: Found 1 midi files and 1 pdf files. test cases: Case 1 - A single score and no \midi block: \score { \new Staff \music \layout { } } Case 2 - A single score and a \midi block: \score { \new Staff \music \layout { } \midi { } } Case 3 - Two score blocks without a midi block: \score { \new Staff \music \layout { } } \score { \new Staff \music \layout { } } Case 4 - Two score blocks with a midi block: \score { \new Staff \music \layout { } } \score { \new Staff \music \midi { } } Case 5 - Something esoteric without a midi block: \book { \bookOutputName "pdf_file_only" \score { \new Staff \music \layout {} } } \book { \bookOutputName "midi_file_only" \score { \new Staff \music \layout {} } } Case 6 - Something esoteric with a midi block: \book { \bookOutputName "pdf_file_only" \score { \new Staff \music \layout {} } } \book { \bookOutputName "midi_file_only" \score { \new Staff \music \midi {} } } The test script looks like this: #!/bin/sh export PATH=/path/to/lilypond-2.24.3/bin:$PATH rm -rf *.pdf *.midi LIST="1 2 3 4 5 6" for k in $LIST; do rm -rf *.pdf *.midi echo -n "Case $k: " lilypond -s -dinclude-settings=add-midi.ily "case$k.ly" PDFS=`ls 2>/dev/null -1 *.pdf | wc -l` MIDIS=`ls 2>/dev/null -1 *.midi | wc -l` echo Found $MIDIS midi files and $PDFS pdf files. done rm -rf *.pdf *.midi And add-midi.ily looks like this: #(define (add-midi-to-score score) (define (has-midi? score) (any (lambda (x) (ly:output-def-lookup x 'is-midi)) (ly:score-output-defs score))) (if (has-midi? score) score #{ \score { $score \midi {} } #})) toplevel-score-handler = #(lambda (score) (collect-scores-for-book (add-midi-to-score score))) music = { \relative c'' \repeat volta 20 { c4 c c c \break } }
Re: lilypond "preprocessor"?
Aaron Hill writes: > On 2023-12-12 1:04 pm, David Kastrup wrote: >> Aaron Hill writes: >>> >>> #(define (add-midi-to-score score) >>> (define (has-midi? score) >>> (any (lambda (x) (ly:output-def-lookup x 'is-midi)) >> Wouldn't that need to be (ly:output-def-lookup x 'is-midi #f) ? > > The default is to return '() which I thought was falsey. In LISP it would be. In Scheme, it isn't. `any' is a Scheme function. -- David Kastrup
Re: lilypond "preprocessor"?
On 2023-12-12 1:04 pm, David Kastrup wrote: Aaron Hill writes: #(define (add-midi-to-score score) (define (has-midi? score) (any (lambda (x) (ly:output-def-lookup x 'is-midi)) Wouldn't that need to be (ly:output-def-lookup x 'is-midi #f) ? The default is to return '() which I thought was falsey. -- Aaron Hill
Re: lilypond "preprocessor"?
Aaron Hill writes: > On 2023-12-12 12:06 pm, Stefano Antonelli wrote: >> On Tue, 2023-12-12 at 02:31 -0800, Aaron Hill wrote: >>> Would this not work? >>> >>> #(define (add-midi-to-score score) >>>#{ \score { $score \midi {} } #}) >>> toplevel-score-handler = >>> #(lambda (score) >>>(collect-scores-for-book (add-midi-to-score score))) >>> >> Indeed it does! >> Without a \midi block one midi file is produced. >> However, if there is already a \midi block, two midi files are >> produced. I'm not sure if that's going to be a problem aside from the >> extra processing time. > > Any better? > > > #(define (add-midi-to-score score) > (define (has-midi? score) > (any (lambda (x) (ly:output-def-lookup x 'is-midi)) Wouldn't that need to be (ly:output-def-lookup x 'is-midi #f) ? > (ly:score-output-defs score))) > (if (has-midi? score) score > #{ \score { $score \midi {} } #})) -- David Kastrup
Re: lilypond "preprocessor"?
On 2023-12-12 12:06 pm, Stefano Antonelli wrote: On Tue, 2023-12-12 at 02:31 -0800, Aaron Hill wrote: Would this not work? #(define (add-midi-to-score score) #{ \score { $score \midi {} } #}) toplevel-score-handler = #(lambda (score) (collect-scores-for-book (add-midi-to-score score))) Indeed it does! Without a \midi block one midi file is produced. However, if there is already a \midi block, two midi files are produced. I'm not sure if that's going to be a problem aside from the extra processing time. Any better? #(define (add-midi-to-score score) (define (has-midi? score) (any (lambda (x) (ly:output-def-lookup x 'is-midi)) (ly:score-output-defs score))) (if (has-midi? score) score #{ \score { $score \midi {} } #})) -- Aaron Hill
Re: lilypond "preprocessor"?
On Tue, 2023-12-12 at 02:31 -0800, Aaron Hill wrote: > Would this not work? > > > #(define (add-midi-to-score score) >#{ \score { $score \midi {} } #}) > > toplevel-score-handler = > #(lambda (score) >(collect-scores-for-book (add-midi-to-score score))) > Indeed it does! Without a \midi block one midi file is produced. However, if there is already a \midi block, two midi files are produced. I'm not sure if that's going to be a problem aside from the extra processing time. If there are multiple \score blocks, I don't know what would happen... ly2video is only intended to process a single score. Or perhaps even just a single instrument. And tries to do so on files that already produce a pdf. There is another project called LilypondToBandVideoConverter which outputs multiple midi tracks and multiple videos. It requires a unique lilypond file that won't compile to anything on it's own. It just puts the music in predefined variables that the python code then puts into a purpose built lilypond file to create what it needs.
Re: lilypond "preprocessor"?
On Tue, 2023-12-12 at 11:00 +0100, Jean Abou Samra wrote: > > cat first.ly second.ly third.ly > out.ly > > lilypond out.ly > > But it's not portable. I'd have to do the equivalent in python if > > there's no way to do it with lilypond. > > Not sure I understand the need, why do you need to append at the end, > as opposed to prepending with -dinclude-settings? (There is no > equivalent of -dinclude-settings to append.) Mainly to override the \paper and \header blocks that are in the user's ly file. Because later definitions will replace earlier ones. Especially in the case of \override. I think. At the moment ly2video removes the whole \paper block (it searches and matches { and }) and then adds it's own. Whereas, it should be possible to 'reset' all \paper blocks by appending one to the end. Speaking of which, is there a way to reset an entire block to defaults that doesn't require setting each individual property? For the \header block, it would be enough to suppress printing of the header fields and any header or footer if there were functions to do so. Rather than changing \header values to #f. That would work better when \header is defined at the \score level. The \paper block, as I'm now reading, can be placed in three different places (descending hierarchy). So it would suffer from the same problem as \header. A single \paper block appended to the end of the user's ly file isn't robust enough. At the same time deleting \paper blocks from the input file doesn't work with \paper blocks included from other files. Also to place other things like changing the staff size. The user could have placed: #(set-global-staff-size 40) in their song.ly (or in some included file) and ly2video wants to set it back to the default: #(set-global-staff-size 20) The last thing ly2video does that I'm struggling with is ly2video removes any \bookOutputName that it sees. Presumably this is to make sure the name of the midi or pdf/png filename doesn't change. The user could, for example, supply a file that looks like this: \header { title = "File title" midititle = "midi title" } music = \relative c' { c1 } \book { \bookOutputName "pdf_file_only" \score { \new Staff \music \layout {} } } \book { \bookOutputName "midi_file_only" \score { \new Staff \music \midi {} } } In this case, ly2video would process it fine *if* the \bookOutputNames were deleted (which is what it does). It's the same music in both cases, so the png output and the midi output would match. Especially with that unroll repeats magic you posted earlier prepended. I found that example in the answer to a stackoverflow question about how to name the pdf and midi files differently. I don't know if it's common "out in the wild". The removal of \bookOutputNames precedes hosting ly2video on github. It's in the initial zip file import from Nov 17, 2012. I don't know why it was added. I don't want to break any existing functionality. And some of the existing functionality doesn't work when it comes from included files and ly2video can't see it.
Re: lilypond "preprocessor"?
On 2023-12-12 2:01 am, Jean Abou Samra wrote: One thing occurred to me. If the original \score did not have a \midi block, would it be possible to ask lilypond to produce midi output without modifying the input file? No, this is not currently possible, although it would be a nice addition. Would this not work? #(define (add-midi-to-score score) #{ \score { $score \midi {} } #}) toplevel-score-handler = #(lambda (score) (collect-scores-for-book (add-midi-to-score score))) -- Aaron Hill
Re: lilypond "preprocessor"?
>> One thing occurred to me. If the original \score did not have a >> \midi block, would it be possible to ask lilypond to produce midi >> output without modifying the input file? > > No, this is not currently possible, although it would be a nice > addition. Stefano, please file an issue for this in our tracker! Werner
Re: lilypond "preprocessor"?
> One thing occurred to me. If the original \score did not have a \midi > block, would it be possible to ask lilypond to produce midi output > without modifying the input file? No, this is not currently possible, although it would be a nice addition. signature.asc Description: This is a digitally signed message part
Re: lilypond "preprocessor"?
> If I can find a way to append to the input file, I can probably get rid > of that scraping/parsing code completely. I came across -dread-file- > list. If I understand I would need to create a file: > > ``` > $ cat list-of-files.txt > first.ly > song.ly > last.ly > ``` > > And then > > ``` > lilypond -dread-file-list list-of-files.txt > ``` > > However, this doesn't join all the files into one long file. It > processes each file on it's own. > > Right now I can do > > ``` > cat first.ly second.ly third.ly > out.ly > lilypond out.ly > ``` > > But it's not portable. I'd have to do the equivalent in python if > there's no way to do it with lilypond. Not sure I understand the need, why do you need to append at the end, as opposed to prepending with -dinclude-settings? (There is no equivalent of -dinclude-settings to append.) signature.asc Description: This is a digitally signed message part
Re: lilypond "preprocessor"?
On Mon, 2023-12-11 at 17:22 +0100, Jean Abou Samra wrote: > ly2video or not, you need to unfold repeats as soon as you want > correct MIDI output. I agree that requiring to do it explicitly is > not very user-friendly, but auto-adding \unfoldRepeats sounds like > solving the problem at the wrong level. Also, what ly2video does is > not always correct since \score { \midi { ... } \music } is valid but > \score { \unfoldRepeats \midi { ... } \music } is not — it would need > to be \score { \midi { ... } \unfoldRepeats \music }. One thing occurred to me. If the original \score did not have a \midi block, would it be possible to ask lilypond to produce midi output without modifying the input file? At present, if there is no midi file produced, ly2video fails. That means the user has to at least edit the file to add midi output. It would be nice to avoid that.
Re: lilypond "preprocessor"?
On Mon, 2023-12-11 at 19:41 +0100, Jean Abou Samra wrote: > Put > > break = {} > pageBreak = {} > in the -dinclude-settings file? Wow. That's deceptively simple! > You can put > > #(set! toplevel-music-functions (cons unfoldRepeats toplevel-music- > functions)) > in -dinclude-settings. This is fantastic! I only briefly tested it and the page breaks and I think almost all of it can be done via -dinclude-settings. Thank you so much. If I can find a way to append to the input file, I can probably get rid of that scraping/parsing code completely. I came across -dread-file- list. If I understand I would need to create a file: $ cat list-of-files.txt first.ly song.ly last.ly And then lilypond -dread-file-list list-of-files.txt However, this doesn't join all the files into one long file. It processes each file on it's own. Right now I can do cat first.ly second.ly third.ly > out.ly lilypond out.ly But it's not portable. I'd have to do the equivalent in python if there's no way to do it with lilypond.
Re: lilypond "preprocessor"?
> There is a case where I want to create a narrower page for a narrower > screen. I can easily append a /paper {} block to the original file to > do this. However, any manual breaks will probably not work at this > newer width. How would you recommend dealing with this? Put ``` break = {} pageBreak = {} ``` in the `-dinclude-settings` file? > I don't disagree. It would be great if lilypond had an option to > unfold everything, but I suspect that it's hard to do? In fact, I realized it's already possible. You can put ``` #(set! toplevel-music-functions (cons unfoldRepeats toplevel-music-functions)) ``` in `-dinclude-settings`. signature.asc Description: This is a digitally signed message part
Re: lilypond "preprocessor"?
On Mon, 2023-12-11 at 17:22 +0100, Jean Abou Samra wrote: > > We need a copy so we can introduce the space time dumper code which > > boils down to a simple /include statement at the very top of the > > file. > > You can do that via the -dinclude-settings option instead. Good to know, thanks. > > It looks like ly2video also removes any \break, \noBreak, and > > \pageBreak it finds in the input. > > > > I don't understand why it needs to do this. \break and \pageBreak are > ignored with #ly:one-line-breaking anyway. I'd guess #ly:one-line-breaking didn't exist in the beginning. If #ly:one-line-breaking ignores breaks, then yes there's probably no need to remove them. I'll do some testing. There is a case where I want to create a narrower page for a narrower screen. I can easily append a /paper {} block to the original file to do this. However, any manual breaks will probably not work at this newer width. How would you recommend dealing with this? > ly2video or not, you need to unfold repeats as soon as you want > correct MIDI output. I agree that requiring to do it explicitly is > not very user-friendly, but auto-adding \unfoldRepeats sounds like > solving the problem at the wrong level. Also, what ly2video does is I don't disagree. It would be great if lilypond had an option to unfold everything, but I suspect that it's hard to do? > not always correct since \score { \midi { ... } \music } is valid but > \score { \unfoldRepeats \midi { ... } \music } is not — it would need > to be \score { \midi { ... } \unfoldRepeats \music }. To be fair, all the examples I've seen put the \midi block after the music. Usually after \layout or in place of \layout if no print output is required. In the case of the second example, the user would probably get an error message that wasn't terribly helpful. I will at least add what you said as a comment in the code. Certainly in my own usage, I've never faced that issue. And it doesn't appear to be an open issue in github. Unless there's a better way, it's probably not the worst thing to do. > Lastly, I'm not sure why ly2video wants to parse the header block but > the --header option may help. l2video wants to parse the header block so it can create a title page for the video. ly2video doesn't want lilypond to put a title on the score. So after scraping the \header fields, it doesn't write those \header fields to the file it ultimately sends to lilypond to process. No \header means no title in the score. No headers and footers are desired too. I know that #ly:one-line-breaking omits these things, but #ly:one-page- breaking may not. So there's still a need to deal with the \header fields. That --header option looks like just the thing to get desired details in my case. I will do some testing.
Re: lilypond "preprocessor"?
On Mon, 2023-12-11 at 03:49 -0800, Aaron Hill wrote: > Why should ly2video need any such smarts at all? Since there are > myriad > ways an end user can structure their input files, let LilyPond do its > job and have ly2video ask it for all the information needed. It > sounds > like ly2video is already injecting code to add new engravers and what > not for extracting timing details. Do the same but dump all of the > relevant header fields while you are at it. At this point, ly2video > only needs to parse this extracted data and can completely ignore > what > the end user is doing in their source. ly2video was started in 2012. I have no idea what the capabilities of lilypond were at that time. That's the short answer. Yes it would be nice if ly2video could completely ignore what the end user is doing in their source. If possible.
Re: lilypond "preprocessor"?
> We need a copy so we can introduce the space time dumper code which > boils down to a simple /include statement at the very top of the file. You can do that via the `-dinclude-settings` option instead. > It looks like ly2video also removes any \break, \noBreak, and > \pageBreak it finds in the input. I don't understand why it needs to do this. `\break` and `\pageBreak` are ignored with `#ly:one-line-breaking` anyway. > > Normally, you'd spell out the \unfoldRepeats and stuff in the input > > file directly. > > I don't think the user should do this. What I mean is that it's not > user friendly. ly2video or not, you need to unfold repeats as soon as you want correct MIDI output. I agree that requiring to do it explicitly is not very user-friendly, but auto-adding `\unfoldRepeats` sounds like solving the problem at the wrong level. Also, what ly2video does is not always correct since `\score { \midi { ... } \music }` is valid but `\score { \unfoldRepeats \midi { ... } \music }` is not — it would need to be `\score { \midi { ... } \unfoldRepeats \music }`. Lastly, I'm not sure why ly2video wants to parse the header block but the `--header` option may help. signature.asc Description: This is a digitally signed message part
Re: lilypond "preprocessor"?
On 2023-12-10 9:33 pm, Stefano Antonelli wrote: On Sun, 2023-12-10 at 08:46 -0500, Michael Werner wrote: I'm not at all familiar with ly2video, so I really have no idea if this'll be directly useful to you. But for getting the headers from an included file, this is how I do it with nearly all the music I engrave. Maybe it can serve as a starting point. The issue is that ly2video parses anotherFile.ly directly and it can't see the fields in the header block because they are defined in song.ly. It doesn't have lilypond's smarts to parse "\include song.ly". (Apologies for jumping in here.) Why should ly2video need any such smarts at all? Since there are myriad ways an end user can structure their input files, let LilyPond do its job and have ly2video ask it for all the information needed. It sounds like ly2video is already injecting code to add new engravers and what not for extracting timing details. Do the same but dump all of the relevant header fields while you are at it. At this point, ly2video only needs to parse this extracted data and can completely ignore what the end user is doing in their source. P.S. I should note that I have never used ly2video before, so I am commenting mainly as an outside observer to this use case. -- Aaron Hill
Re: lilypond "preprocessor"?
On Sun, 2023-12-10 at 08:46 -0500, Michael Werner wrote: > And then I would have anotherFile.ly containing something like: > > \include "song.ly" > > \book { > \myHeaders > \score { > \theMusic > } > } Thanks for the suggestion Michael. I like that idea, but it doesn't help with the problem. > I'm not at all familiar with ly2video, so I really have no idea if > this'll be directly useful to you. But for getting the headers from > an included file, this is how I do it with nearly all the music I > engrave. Maybe it can serve as a starting point. The issue is that ly2video parses anotherFile.ly directly and it can't see the fields in the header block because they are defined in song.ly. It doesn't have lilypond's smarts to parse "\include song.ly". Thanks, Stef
Re: lilypond "preprocessor"?
On Sun, 2023-12-10 at 10:56 +0100, Jean Abou Samra wrote: > It would be better for ly2video not to modify the input file. Technically, it modifies a copy of the input file. The scraping is clunky and I don't like it though it seems necessary. The idea, I suppose, is to make it easy for the user. There are quite a few little details that need to be right for making the video. I wouldn't want to put it on the user to enter those details by hand. We need a copy so we can introduce the space time dumper code which boils down to a simple /include statement at the very top of the file. If it's placed after the /score block it doesn't appear to work. What I would prefer to do is append snippets of code to the end of a copy of the user's file rather than scrape the input. If I could add some code that was like this: \layout { \set Score.unroll-all-loops = #t } Then it would be simple to append. But as far as I know that unroll loops has to be done at the \score block in front of the music. Which means interfering with the input file. Basically ly2video looks for: \score { and replaces it with: \score { \unfoldRepeats It looks like ly2video also removes any \break, \noBreak, and \pageBreak it finds in the input. If you're interested, the relevant code lives here: < https://github.com/aspiers/ly2video/blob/41364ad9c5d512c502de2c9b06f7878bd88b77e1/ly2video/cli.py#L1411C1-L1519C1 > > Normally, you'd spell out the \unfoldRepeats and stuff in the input > file directly. I don't think the user should do this. What I mean is that it's not user friendly. The approach I've been perusing is to create a new ly file that includes the user's file. However, this only works if the user's file conforms to a certain format: music defined in a macro, no score block. I don't think it's user friendly. And the header (in a separate file) isn't easy to parse. From what I've seen around most people have a score block in the same file as the music. To make a video from this file, as you suggest, the user would need to either temporarily modify the input file (yuck), or make a copy of that file and modify it (now the music is in two places, yucky). What would be better is to take most of what the user has entered and strip off the things ly2video absolutely don't need. That's what ly2video does, but it also has to inject the unroll loops. Unless there are better ways to do this in a user friendly manner? Ideally the user just does: ly2video -i myfile.ly On any of their files and it just works. -Stef
Re: lilypond "preprocessor"?
Hi Stef, On Sat, Dec 9, 2023 at 11:07 PM Stefano Antonelli < santone...@algosolutions.com> wrote: > Any other options for getting the header block from an included file? > I don't know if this'll be directly usable in your use case, but I do something like this with most of the music I engrave. The way I handle this is my song.ly would be something like: myHeaders = \header { % author and stuff } theMusic = new DrumStaff { % the music } And then I would have anotherFile.ly containing something like: \include "song.ly" \book { \myHeaders \score { \theMusic } } I'm not at all familiar with ly2video, so I really have no idea if this'll be directly useful to you. But for getting the headers from an included file, this is how I do it with nearly all the music I engrave. Maybe it can serve as a starting point. -- Michael
Re: lilypond "preprocessor"?
> I know I can run lilypond on song.ly and it will implicitly wrap it in > a \score block, but is that contained in an intermediate file somewhere > I can get access to? No, there is no such file. The implicit wrapping happens at the level of LilyPond's data structures, it's not a textual macro expansion. It would be better for ly2video not to modify the input file. Normally, you'd spell out the \unfoldRepeats and stuff in the input file directly. signature.asc Description: This is a digitally signed message part