lilypond-user
[Top][All Lists]
Advanced

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

Re: Music function to manage a moment and beat for the Staff and the Voi


From: Volodymyr Prokopyuk
Subject: Re: Music function to manage a moment and beat for the Staff and the Voice
Date: Sun, 2 Jul 2023 14:28:14 +0200

Good afternoon, Jean and Valentin,

Thank you very much for the up to the point explanation, working function, as well as alternatives syntax for object properties using Scheme metaprogramming! Lilypond is quite flexible, but with its own quirks as the handling of default parameters to a music function.

I wonder if there is a way to define a music function with default parameters and be able to selectively specify some parameters using the parameters names leaving all other parameters with their default values? Probably it is asking too much :)

My use case is for the shaped slur function currently defined using the Nunjucks _javascript_ templating engine. The meaning of parameters is: dir = direction, bs = baseline height, sh = horizontal shift, wd = slur width, ht = slur height, dt = delta between left and right ends

{% macro shapedSlur(dir, bs = 2.0, sh = 0.5, wd = 1.0, ht = 1.0, dt = 0.0) %}
  {% set ax = 0.0 * wd + sh %}
  {% set bx = 1.0 * wd + sh %}
  {% set cx = 2.0 * wd + sh %}
  {% set dx = 3.0 * wd + sh %}
  {% set ay, dy = bs %}
  {% set by, cy = (bs + ht) %}
  {% if dir == "fu" %} <# forward up slur #>
    {% set dy = dy + dt %}
  {% elif dir == "fd" %} <# forward down slur #>
    {% set dy = dy + dt %}
    {% set ay = -ay %}
    {% set by = -by %}
    {% set cy = -cy %}
    {% set dy = -dy %}
  {% elif dir == "bu" %} <# backward up slur #>
    {% set ax = ax - 3.0 * wd %}
    {% set bx = bx - 3.0 * wd %}
    {% set cx = cx - 3.0 * wd %}
    {% set dx = dx - 3.0 * wd %}
    {%set ay = ay + dt %}
  {% elif dir == "bd" %} <# backward down slur #>
    {% set ax = ax - 3.0 * wd %}
    {% set bx = bx - 3.0 * wd %}
    {% set cx = cx - 3.0 * wd %}
    {% set dx = dx - 3.0 * wd %}
    {%set ay = ay + dt %}
    {% set ay = -ay %}
    {% set by = -by %}
    {% set cy = -cy %}
    {% set dy = -dy %}
  {% endif %}
  \tweak control-points
    #'(({{ ax }} . {{ ay }}) ({{ bx }} . {{ by }})
       ({{ cx }} . {{ cy }}) ({{ dx }} . {{ dy }}))
{% endmacro %}

I can easily specify only arguments that differ from default values like below

{{ shapedSlur("fd", bs = 3) }} ( <>)
{{ shapedSlur("fu", bs = 0, wd = 2.2, ht = 2.8) }} ( <>)

The current solution depends on the Nunjucks external template engine. It would be nice to define this logic using a music function with the syntax similar to the below (not yet working code) and ideally not having to specify the \default placeholder for every not modified argument. The last parameter is mandatory and indicates the direction of a slur

\shapedSlur bs = 3 "fd" ( <>)
\shapedSlur bs = 0 wd = 2.2 ht = 2.8 "fu" ( <>)

Probably the above is too challenging or maybe there is an alternative equally concise idiomatic solution in LilyPond.

Thank you very much!
Vlad

On Sat, Jul 1, 2023 at 5:58 PM Valentin Petzel <valentin@petzel.at> wrote:
Hello Vlad,

in addition to Jean’s answer it might be helpful to understand what

Staff.property

actually means. This get’s parsed to a list of the symbols 'Staff and
'property. So you can in fact simply remain with scheme, and directly enter
#(list scope 'property) like this

%%%%%
\version "2.24.1"

momentBeat =
#(define-music-function (scope moment beat) ((symbol? 'Staff) fraction? list?)
   #{
     \set #(list scope 'beamExceptions) = #'()
     \set #(list scope 'baseMoment) = #(ly:make-moment (/ (car moment) (cdr
moment)))
     \set #(list scope 'beatStructure) = #beat
   #})

{
  \momentBeat Voice 1/8 3,3,2
  c'8 8 8 8 8 8 8 8
}
%%%%%

or if you enjoy abusing (quasi)quoting by doing #`(,scope property) like this

%%%%%
\version "2.24.1"

momentBeat =
#(define-music-function (scope moment beat) ((symbol? 'Staff) fraction? list?)
   #{
     \set #`(,scope beamExceptions) = #'()
     \set #`(,scope baseMoment) = #(ly:make-moment (/ (car moment) (cdr
moment)))
     \set #`(,scope beatStructure) = #beat
   #})

{
  \momentBeat Voice 1/8 3,3,2
  c'8 8 8 8 8 8 8 8
}
%%%%%

Cheers,
Valentin

Am Samstag, 1. Juli 2023, 16:58:48 CEST schrieb Jean Abou Samra:
> Le samedi 01 juillet 2023 à 16:42 +0200, Volodymyr Prokopyuk a écrit :
> > I'm trying to define a music function as below, however I've faced
> > difficulties with parameter predicates, visibility of the Staff object,
> > and flexibility of the solution ideally without code duplication
> >
> > **Desired music function** (the code is not working)
> >
> > ```
> >   momentBeat = #(define-music-function (moment beat scope)
> >     (fraction? list? (what? Staff))
> >     #{
> >       \set #scope.beamExceptions = #'()
> >       \set #scope.baseMoment = #(ly:make-moment moment)
> >       \set #scope.beatStructure = #beat
> >     #})
> > ```
> >
> > **Usage**
> >
> > ```
> > \momentBeat 1/2 #'(1)
> > \momentBeat 1/4 1,1,1,1 Voice
> > ```
> >
> > **Questions**
> >
> >
> > - It seems that Staff is not available to Guile at that moment. I tried to
> > use "Staff" string and 'Staff symbol for the scope parameter with a cond
> > _expression_ and code replication for Staff and Voice, but I did not manage
> > to get it to work due to incorrect predicates errors
> >
> > - How would it look like an idiomatic and working implementation of the
> > above music function?
> There are a couple problems here.
>
> - The `#` character introduces a Scheme _expression_. In Scheme, dots are
> allowed in identifiers. As such, `\set #scope.beamExceptions` is trying to
> look up a variable literally called "scope.beamExceptions". You need a
> space after `#scope` to terminate the Scheme _expression_. - A `fraction?` is
> not a rational number but a (numerator . denominator) pair. The reason is
> that `fraction?` is the predicate for music functions like `\time`, and
> `\time 4/4` needs to be distinguished from `\time 2/2` (for example). The
> `ly:make-moment` function, on the other hand, expects a rational number. -
> If you want an optional argument to actually be optional, it must not be
> the last argument. Otherwise, you can't actually omit it, you can only
> replace it with `\default`. See
> [here](https://extending-lilypond.gitlab.io/en/extending/music.html#optiona
> l-arguments) for more details.
>
> Here is a working version of your function:
>
> ```
> \version "2.24.1"
>
> momentBeat =
> #(define-music-function (scope moment beat) ((symbol? 'Staff) fraction?
> list?) #{
>      \set #scope .beamExceptions = #'()
>      \set #scope .baseMoment = #(ly:make-moment (/ (car moment) (cdr
> moment))) \set #scope .beatStructure = #beat
>    #})
>
> {
>   \momentBeat Voice 1/8 3,3,2
>   c'8 8 8 8 8 8 8 8
> }
> ```
>
> Best,
>
> Jean


reply via email to

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