lilypond-user
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Defining variables mid-stream in a music expression using tags.


From: dfro
Subject: Re: Defining variables mid-stream in a music expression using tags.
Date: Fri, 19 May 2023 22:55:50 -0400
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Thunderbird/102.10.0

On 5/19/23 05:38, Valentin Petzel wrote:
Hello David,

My conundrum is that I want to call the same music expression (that is
declared as a variable) from different staves, but also cause markup
functions inside the music expression to behave differently from outside
the music expression. Using tags is the only way I have found to do
that. I am glad that I have a method that works. Thank you for clearly
conveying to me that I should keep global variables static!
There is nothing that speaks against using tags for this, but you can do this
totally fine using music function. Not that it is necessary here, but if you
want to create scores in a more dynamic manner at some point going for music
functions is a way to go.

My noob thought is - why not allow custom variables to be declared when
making a \new Staff in its \with statement? These variables would have
the scope of working only on those calls to them that are from within
that particular staff. Different staves could create the same variable
with different values in their \with statement and thus change the
behavior of different functions within the same called music expression.
These variables would take precedence over global variables of the same
name. Please, take or leave.
We can in fact do that to some extent, which is context properties. The
problem with this is again the time at which things are evaluated. Basically
Lilypond works more or less like this:

  1) Parse and evaluate your ly file
  2) Now we should have a collection of scores. For each score:
  3) Iterate over the music expressions and send the events therein to the
relevant engravers in the relevant contexts
  4) We should now have a collection of grobs created by the engravers
  5) Position the grobs
  6) Position the scores
  7) Render everything

Context relevant properties only matter in step 3. The problem is that music
expressions are resolved in step 1, where we do not have such information.

What you are thinking about would need to get the context variables into the
music expression before it is evaluated, so you’d need some sort of
preprocessing step before step 1.

One way to do this would be to write your music function as string and parse
it everywhere you need it

#(define a "1")

myLilyCode = "
  { c'^\markup #a }
"

{ $(ly:parser-include-string myLilyCode) }

#(define a "2")
{ $(ly:parser-include-string myLilyCode) }

But essentially this will provide the same function as a music function, just
more prone to errors. Of course you could also do some non standard evaluation
stuff like this

myCode = #'#{ c^\markup #var #}

#(define var "1")
{ $(primitive-eval myCode) }

#(define var "2")
{ $(primitive-eval myCode) }

Of course, if we want to get funky we can do something like this:

evalWith =
#(define-scheme-function (alist expr) (list? scheme?)
    (primitive-eval `(let ,(map (lambda (x) `(,(car x) ,(cdr x))) alist)
,expr)))

#(define var "1")

expr = #'#{ c'^\markup#var #}

{
  \evalWith #'() \expr
  \evalWith #'((var . "2")) \expr
}

which is like doing a music function, but will allow us to inject any kinds of
variables into an expression, while resolving to global bindings by default.
But I do not think it would be a particularly good idea to do so...

Cheers,
Valentin


Valentin,
Yes! Thank you for the explanation! You are really helping me to sort out some important basic concepts.

I am finally getting what you have been saying all along - that this all can be done with a music function. I was not seeing that the whole music expression for the two staves can be wrapped inside a music function. For some reason I could not see it.
What I want can be accomplished with:


\version "2.24.1"

%Global variables
#(define global-var " gl-var-1")

music =
#(define-music-function
  (local-var)
  (markup?)
  #{
    c'^\markup \concat { #local-var #global-var }
  #})

\new StaffGroup
<<
  \new Staff
  { \music "lc-var-1" }
  \new Staff
  { \music "lc-var-2" }
>>



I do have a difference in the the output of the code below, which does not use \new Staff.

Without \textLengthOn, it makes two staves. With \textLenthOn, it combines the two music expressions into one stave. Why is that happening?


\version "2.24.1"

%Global variables
#(define global-var " gl-var-1")

music =
#(define-music-function
  (local-var)
  (markup?)
  #{
    \textLengthOn
    c'^\markup \concat { #local-var #global-var }
    c'^\markup \concat { #local-var #global-var }
  #})

\new StaffGroup
<<
{ \music "lc-var-1" }
{ \music "lc-var-2" }
>>


I like your "non-music function" examples. I see that your second and third example use primitive-eval. What does primitive-eval do and where could I read about it?

The third example uses scheme code that is beyond my comprehension, at the moment. I do not understand how you declared the music expression with myCode = #'#{ ... #}.  I have not seen that construction before.


Thank you for helping me write better code.


Peace,

David




reply via email to

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