lilypond-user
[Top][All Lists]
Advanced

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

Re: LilyPond to Scheme to Lilypond and variadic function


From: Daniel Tomas
Subject: Re: LilyPond to Scheme to Lilypond and variadic function
Date: Thu, 26 Nov 2020 07:27:02 +0800

Thanks a lot. 
You help advance a lot, but still i have problems to resolve.
(First question is : is it better to answer before or after what we have written before ?)
I try to use optional arguments, but may be it would not be possible from within LilyPond code call this function. I give that example :
upper = {
  \time 6/8
  ...
  <g' b'>4 <g' b'>8 <b' d''>8. <a' c''>16 <g' b'>8 |
  <f' a'>8. <g' b'>16 <f' a'>8 <d' f'>4 r8 |
 
  % \MTKey JM d'4 Hello ##t e'4 #'(9 . 8)
  \MTKeyExp JM d'4 Hello ##t e'4 2 3  %ok
  \MTKeyExp JM d'4 Hello ##t   %wrong because following c' d' e'
  c' d' e'
}
Now i give definition of MTKeyExp :
MTKeyExp =  #(define-music-function
            (parser location name mainNote textBelowMain cyclic . rest)
            (string? ly:music? string? boolean? ly:music? number? number?)
    (let-optional rest ((note1 d'4) (num1 2) (den1 1))
   
     #{
       \key c \major
       \cadenzaOn
        ...
  \setAlterationNote #num1 #den1 #note1
...
  \setRightBracket s4
  \cadenzaOff
 ...
     #}
     ))
My idea was to use a conditional call giving  note1 a default value of null :
setAlterationNote =
#(define-music-function
        (parser location num den note)
        (number? number? ly:music?)
       
        (let
         ;;((stencil (ly:stencil-add (ly:stencil-add altV (getAltPlaceNumStencil 1 2)) (getAltPlaceNumStencil 2 -3))))
         ((stencil (ly:stencil-add altV      
                 (do ((primes '(2 3 5 7 11 13 17 19 23) (cdr primes))
                     (place 0 place)
                     (st altV (ly:stencil-add st (getAltPlaceNumStencil place  (- (ntimesdivide num (car primes)) (ntimesdivide den (car primes)))))))
                 ((null? primes) st)
                 (set! place (+ place 1))
                 )                                      
                                          )))
         (if (null? note) '()
         
        #{
  \once \override  Stem.stencil = $stencil
  ...
  \grace
  c4  
  #note
        #})))
But you see at beginning that i can not give optional arguments. LilyPond interpret following 3 musical elements as optional parameters of MTKeyExp.
If i use a list as a argument, there will be a mixt of musical expressions and numbers, but would not be possible to use commas ',' because that can be part of a musical _expression_ like "d,4".
Have you some idea to resolve this ? Thanks a lot for any help. However i am a good C++ programmer, i am newcommer at Scheme, but a big fan of LilyPond, and i will try to use my microtonal expressions through LilyPond. I also already plane how to change that midi file in order to listen correct tunings. But this step which i ask you is like a stone for my.

Daniel Tomas

On Wed, Nov 25, 2020 at 8:44 AM Aaron Hill <lilypond@hillvisions.com> wrote:
On 2020-11-24 3:00 pm, Daniel Tomas wrote:
> I am working on a Microtonal key for LilyPond, but I need help to
> construct
> a function which I will try to simplify here. I have some questions :
> 1) In Scheme can be possible to write a function which is variadic with
> a
> point :
> ( function (arg1 arg2 . m )  ( ... ))
> And here  m contains not only one parameter passed. m1, m2, ...
> I have not found in any place information about how to access m, is it
> a
> list ? But in this case, we can directly use a list like
> (function (arg1 arg2 ls) ( ... ))

m is a list, although there are helpers for destructuring it.  This is
documented in the Guile manual [1].

[1]: https://www.gnu.org/software/guile/docs/docs-1.8/guile-ref/

%%%%
\version "2.20.0"

#(define (func first second . rest)
   (let-optional rest ((third 'default) (fourth 'default))
    (format #t "\n -- func --")
    (format #t "\n     required args: first=~s, second=~s" first second)
    (format #t "\n destructured args: third=~s, fourth=~s" third fourth)
    (format #t "\n   additional args: rest=~s" rest)))

#(func 'a 'b)
#(func 'c 'd 'e)
#(func 'f 'g 'h 'i 'j)
%%%%
====
Parsing...
  -- func --
      required args: first=a, second=b
  destructured args: third=default, fourth=default
    additional args: rest=()
  -- func --
      required args: first=c, second=d
  destructured args: third=e, fourth=default
    additional args: rest=()
  -- func --
      required args: first=f, second=g
  destructured args: third=h, fourth=i
    additional args: rest=(j)
Success: compilation successfully completed
====

The advantage of (lambda args) over (lambda (args)) is that the list is
implicit.  Callers can invoke the procedure more naturally.  Imagine if
one had to say (+ (list 1 2 3)) instead of simply (+ 1 2 3).


> 2) Now i need to do this with music information like :
> Function =  #(define-music-function
>             (parser location cyclic  ls)
>             (boolean? list?)
>      #{
>       ...
> How call to another function which arguments are musical elements and 2
> numbers passed n times by <ls> ?
>      ...
>      #}
>      )
>
> Function need to be called with unfixed number N of arguments :
> \Function #t c4 2 3 d 2 5 e 4 1 f2 1 1 ... musicN nN1 nN2
> Please , somebody can help me ?

The above cannot be done as LilyPond's scheme functions cannot handle
such open-ended arity.  The only variability is optional arguments, and
even then there are some restrictions due to how the parser works.

To provide arbitrary numbers of things, you must use a list of some
form.

%%%%
\version "2.20.0"

foo =
#(define-void-function (args) (list?) (format #t "\n args=~s" args))

\foo #'(this is a literal scheme list)
\foo 3,1,4,1,5,9,2,6 % ..a list of natural numbers
\foo This.Is.A.Symbol.List

baz =
#(define-void-function (prop args) (symbol? ly:music?)
   (set! args (map (lambda (m) (ly:music-property m prop))
                   (extract-typed-music args 'rhythmic-event)))
   (format #t "\n args=~s" args))

\baz pitch { c d e f }
\baz duration { 1 2. 4 }
\baz text \lyricmode { Here are some words. }
%%%%
====
Parsing...
  args=(this is a literal scheme list)
  args=(3 1 4 1 5 9 2 6)
  args=(This Is A Symbol List)
  args=(#<Pitch c > #<Pitch d > #<Pitch e > #<Pitch f >)
  args=(#<Duration 1 > #<Duration 2. > #<Duration 4 >)
  args=("Here" "are" "some" "words.")
Success: compilation successfully completed
====

With \foo, we are leveraging some of LilyPond's syntactic elements that
ultimately resolve to a list.  There are limitations on what types of
data can appear, but it is certainly an option to consider.

\baz demonstrates working with music as a container for data.  This
option is ideal when inputting a variable number of musical things as it
largely resembles other LilyPond syntax.


-- Aaron Hill

reply via email to

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