Re: Conditional code in the midi block

2024-05-20 Thread Carolyn Beeton
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

2024-05-20 Thread David Kastrup
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

2024-05-20 Thread Thomas Morley
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

2024-05-20 Thread Jean Abou Samra

> 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

2024-05-20 Thread Jean Abou Samra

> 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

2024-05-20 Thread Timothy Lanfear

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

2024-05-19 Thread 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