Re: Conditional code in the midi block
Thank you all for your suggestions. I finally have the flag working after years of just editing my master file when I wanted one track per voice! Thanks, Carolyn > On May 20, 2024, at 6:09 AM, Jean Abou Samra wrote: > > >> nostaffmidi = >> #(define-scheme-function () () >> (if TrackPerVoice #{ \context { \Staff \remove "Staff_performer" } #})) > > > Ah, that proves me wrong. I didn't recall the parser was smart enough for > this. > > >> Although, I don't understand why the contents of the scheme functions cannot >> be placed directly into the midi block like this >> >> \midi { >> #(if TrackPerVoice #{ \context { \Staff \remove "Staff_performer" } #}) >> #(if TrackPerVoice #{ \context { \Voice \consists "Staff_performer" } #}) >> } > > > It works with $ instead of # , as explained in my other post. > > >
Re: Conditional code in the midi block
Jean Abou Samra writes: > Alternatively, you can do it more like you were envisioning, but for > technical reasons, you have to start the Scheme code with $ , not # , > and you also have to include an extra \midi { } around the \context > blocks (basically because \context outside \layout or \midi is a > different command). > The # vs $ issue is explained here: > https://extending-lilypond.gitlab.io/en/extending/lily-and-scheme.html#hash-vs-dollar No, it isn't. What happens here is actually not as much a "technical reason" as a design decision. The difference between $ and # explained in the manual entry is orthogonal to it. Here is the rundown: $ produces a token category based on the type of the expression. That means that the type of the expression can change the way the parser parses an expression, and that can be important since the parser may use lookahead to make its decisions, and that may mean that the type of the token (and thus the underlying expression) may need to be known before the previous expression is given its place in the grammar. # does produce a single token category. That means that the parsing can go ahead without even knowing the value of the expression, and the expression is only evaluated once its value is actually needed, making for more predictable evaluation order. That much is sort of in the manual. But what isn't there is that whenever # is going to be accepted, there must be an explicit rule in the grammar accepting it, and that essentially means that when that rule is considered applicable, the grammar is fixed. That still is sort of in the manual. But here is where the design decision comes into play: Scheme may be executed for the purpose of creating a side effect, like assigning a value to a variable. When a Scheme function is being called, it doesn't know whether its return value is going to get used for anything, and a Scheme function executed for its side effect may or may not return a meaningful value. The most prominent side effect is a variable assignment. So as kind of a design rule, at any place in the grammar where you could conceivably write an assignment, the value returned by #... is getting ignored because the expression may only have been called for its side effect, like an assigment. Now $ should never be called just for a side effect (because it is kind of a sledgehammer you should not be using unnecessarily). Function calls introduced by \ are explicitly declared to be of the kind where LilyPond knows when it is supposed to be using the return value. And calling a variable by its name \... does not cause a side effect, so it is safe to assume that you are not invoking its name for the purpose of getting a side effect. So this "I am ignoring the result in contexts where assignments may occur" rule only is made to affect # as a way of invoking Scheme expressions. Where and how should we document/put this rationale so that future contributors and power users are able to understand the current behavior and make qualified decisions about future behavior? I am not sold on this being the best/simplest/most logical that LilyPond can be made to behave. But at the current point of time, I don't know how to do better. And when someone wants to change it, I'd like them to be aware of the rationale behind the current behavior. -- David Kastrup
Re: Conditional code in the midi block
Am So., 19. Mai 2024 um 21:28 Uhr schrieb Carolyn Beeton : > > I would like to include some Staff and Voice context settings in the \midi > block only if a flag is set to ##t and I have not been able to figure out how > to do this. When I try to wrap the \context blocks in a code block with > #{…#} I get this error: > trackPerVoiceMWE.ly:31:16: error: syntax error, unexpected '{', expecting > SCM_IDENTIFIER or SCM_TOKEN or STRING or SYMBOL > \context { > Is there any way to conditionally change the Staff_performer based on a flag > setting? > > This is my non-working MWE: > > \version "2.24.0" > \include "english.ly" > > global = { > \key c \major > \time 4/4 > } > TrackPerVoice = ##t > \score { > \new ChoirStaff << > \new Staff << > \new Voice = "soprano" << > \global > \relative c' { c'4 d e f << g1 \\ { g4 f e2 } >> } > >> > \new Voice = "alto" << > \global > \voiceTwo > \relative c' { c'4 b a g | f e d2 } > >> > >> > >> > \layout {} > \midi { > #(display (format #f "TrackPerVoice is ~a\n" TrackPerVoice)) > #(if TrackPerVoice #{ > #(display "One track per Voice (may make too many voices with > polyphony)") > #(display "Turn this off to get sop and alto combined in one track.") > \context { > \Staff > \remove "Staff_performer" > } > \context { > \Voice > \consists "Staff_performer" > } > #} ) > } > } > > Thanks, > Carolyn Here my take on this. Modeled after `enablePolymeter`. Prpbably overkill, though... \version "2.24.3" enableVoicePerformer = #(define-void-function (do-it) (boolean?) (when do-it (display "One track per Voice (may make too many voices with polyphony)") (display "Turn this off to get sop and alto combined in one track.") (let ((module (current-module)) (cmod-remove (ly:make-context-mod)) (cmod-consists (ly:make-context-mod))) (if (not (output-module? module)) (ly:parser-error (G_ "Not in an output definition"))) (ly:add-context-mod cmod-remove (list 'remove 'Staff_performer)) (ly:add-context-mod cmod-consists (list 'consists 'Staff_performer)) ;; FIXME: any chance to use ly:output-find-context-def here? The ;; problem is that we don't have access to the context def, just ;; its scope (module). (module-map (lambda (_sym var) (if (variable-bound? var) (let ((cdef (variable-ref var))) (if (ly:context-def? cdef) (let* ((context-name (ly:context-def-lookup cdef 'context-name)) (aliases (ly:context-def-lookup cdef 'aliases)) (all-names (cons context-name aliases))) (cond ((memq 'Staff all-names) (variable-set! var (ly:context-def-modify cdef cmod-remove))) ((memq 'Voice all-names) (variable-set! var (ly:context-def-modify cdef cmod-consists) module global = { \key c \major \time 4/4 } TrackPerVoice = ##t \score { \new ChoirStaff << \new Staff \with { midiInstrument = "acoustic grand" } << \new Voice = "soprano" \with { midiInstrument = "marimba" } << \global \relative c' { c'4 d e f << g1 \\ { g4 f e2 } >> } >> \new Voice = "alto" \with { midiInstrument = "violin" } << \global \voiceTwo \relative c' { c'4 b a g | f e d2 } >> >> >> \layout {} \midi { \enableVoicePerformer \TrackPerVoice } } Cheers, Harm
Re: Conditional code in the midi block
> nostaffmidi = > #(define-scheme-function () () >(if TrackPerVoice #{ \context { \Staff \remove "Staff_performer" } #})) Ah, that proves me wrong. I didn't recall the parser was smart enough for this. > Although, I don't understand why the contents of the scheme functions cannot > be placed directly into the midi block like this > > \midi { > #(if TrackPerVoice #{ \context { \Staff \remove "Staff_performer" } #}) > #(if TrackPerVoice #{ \context { \Voice \consists "Staff_performer" } #}) > } It works with $ instead of # , as explained in my other post. signature.asc Description: This is a digitally signed message part
Re: Conditional code in the midi block
> I would like to include some Staff and Voice context settings in the > \midi block only if a flag is set to ##t and I have not been able to > figure out how to do this. When I try to wrap the \context blocks > in a code block with #{…#} I get this error: > trackPerVoiceMWE.ly:31:16: error: syntax error, unexpected '{', expecting > SCM_IDENTIFIER or SCM_TOKEN or STRING or SYMBOL > \context { Probably the easiest way is to include or exclude a whole \midi block like this: \version "2.24.0" \include "english.ly" global = { \key c \major \time 4/4 } TrackPerVoice = ##t \score { \new ChoirStaff << \new Staff << \new Voice = "soprano" << \global \relative c' { c'4 d e f << g1 \\ { g4 f e2 } >> } >> \new Voice = "alto" << \global \voiceTwo \relative c' { c'4 b a g | f e d2 } >> >> >> \layout { } #(if TrackPerVoice (begin (display "One track per Voice (may make too many voices with polyphony)") (display "Turn this off to get sop and alto combined in one track.") #{ \midi { \context { \Staff \remove "Staff_performer" } \context { \Voice \consists "Staff_performer" } } #}) #{ \midi { } #}) } Alternatively, you can do it more like you were envisioning, but for technical reasons, you have to start the Scheme code with $ , not # , and you also have to include an extra \midi { } around the \context blocks (basically because \context outside \layout or \midi is a different command). \version "2.24.0" \include "english.ly" global = { \key c \major \time 4/4 } TrackPerVoice = ##t \score { \new ChoirStaff << \new Staff << \new Voice = "soprano" << \global \relative c' { c'4 d e f << g1 \\ { g4 f e2 } >> } >> \new Voice = "alto" << \global \voiceTwo \relative c' { c'4 b a g | f e d2 } >> >> >> \layout { } \midi { $(if TrackPerVoice (begin (display "One track per Voice (may make too many voices with polyphony)") (display "Turn this off to get sop and alto combined in one track.") #{ \midi { \context { \Staff \remove "Staff_performer" } \context { \Voice \consists "Staff_performer" } } #})) } } The # vs $ issue is explained here: https://extending-lilypond.gitlab.io/en/extending/lily-and-scheme.html#hash-vs-dollar HTH Jean signature.asc Description: This is a digitally signed message part
Re: Conditional code in the midi block
On 19/05/2024 20:27, Carolyn Beeton wrote: I would like to include some Staff and Voice context settings in the \midi block only if a flag is set to ##t and I have not been able to figure out how to do this. When I try to wrap the \context blocks in a code block with #{…#} I get this error: trackPerVoiceMWE.ly:31:16: error: syntax error, unexpected '{', expecting SCM_IDENTIFIER or SCM_TOKEN or STRING or SYMBOL \context { Is there any way to conditionally change the Staff_performer based on a flag setting? I think this does what you want. \version "2.24.0" \include "english.ly" TrackPerVoice = ##t nostaffmidi = #(define-scheme-function () () (if TrackPerVoice #{ \context { \Staff \remove "Staff_performer" } #})) voicemidi = #(define-scheme-function () () (if TrackPerVoice #{ \context { \Voice \consists "Staff_performer" } #})) global = { \key c \major \time 4/4 } \score { \new ChoirStaff << \new Staff << \new Voice = "soprano" << \global \relative c' { c'4 d e f << g1 \\ { g4 f e2 } >> } >> \new Voice = "alto" << \global \voiceTwo \relative c' { c'4 b a g | f e d2 } >> >> >> \layout {} \midi { #(ly:message "TrackPerVoice is ~a\n" TrackPerVoice) #(when TrackPerVoice (ly:message "One track per Voice (may make too many voices with polyphony)") (ly:message "Turn this off to get sop and alto combined in one track.")) \nostaffmidi \voicemidi } Although, I don't understand why the contents of the scheme functions cannot be placed directly into the midi block like this \midi { #(if TrackPerVoice #{ \context { \Staff \remove "Staff_performer" } #}) #(if TrackPerVoice #{ \context { \Voice \consists "Staff_performer" } #}) } -- Timothy Lanfear, Bristol, UK.
Conditional code in the midi block
I would like to include some Staff and Voice context settings in the \midi block only if a flag is set to ##t and I have not been able to figure out how to do this. When I try to wrap the \context blocks in a code block with #{…#} I get this error: trackPerVoiceMWE.ly:31:16: error: syntax error, unexpected '{', expecting SCM_IDENTIFIER or SCM_TOKEN or STRING or SYMBOL \context { Is there any way to conditionally change the Staff_performer based on a flag setting? This is my non-working MWE: \version "2.24.0" \include "english.ly" global = { \key c \major \time 4/4 } TrackPerVoice = ##t \score { \new ChoirStaff << \new Staff << \new Voice = "soprano" << \global \relative c' { c'4 d e f << g1 \\ { g4 f e2 } >> } >> \new Voice = "alto" << \global \voiceTwo \relative c' { c'4 b a g | f e d2 } >> >> >> \layout {} \midi { #(display (format #f "TrackPerVoice is ~a\n" TrackPerVoice)) #(if TrackPerVoice #{ #(display "One track per Voice (may make too many voices with polyphony)") #(display "Turn this off to get sop and alto combined in one track.") \context { \Staff \remove "Staff_performer" } \context { \Voice \consists "Staff_performer" } #} ) } } Thanks, Carolyn