lilypond-devel
[Top][All Lists]
Advanced

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

Re: templates and a little bit more


From: Jan-Peter Voigt
Subject: Re: templates and a little bit more
Date: Tue, 10 Apr 2012 13:52:45 +0200
User-agent: Mozilla/5.0 (X11; Linux i686; rv:11.0) Gecko/20120327 Thunderbird/11.0.1

On 06.04.2012 13:17, Janek Warchoł wrote:
On Thu, Apr 5, 2012 at 7:37 PM, Jan-Peter Voigt <address@hidden> wrote:
Dear community,

I bundled some extensions in a little package to try out. Some of these
ideas might be interesting to include in lily.
Here is a start of some docs, but just a little bit. I am working on it ...
http://www.xn--schne-noten-tfb.de/?tabs=3,1
I'm sorry that i don't have time to dig deeper, but my first
impression is that it's somewhat similar to OrchestralLily:
http://lac.linuxaudio.org/2010/papers/25.pdf
http://reinhold.kainhofer.com/orchestrallily/A-simple-example.html#A-simple-example
- it may give you some inspiration.
Yes, my template mechanism has a similar intention. And I think OrchestraLily is a great tool.
But here are two points I wanted to solve differently:
1.: Orchestralily uses "normal" lilypond-variables to store and organize the music. An advantage is a quite simple input.
But a question, I had and read several times on this list remains, why and how to deal with the strict naming rules for lily-vars.
So I started storing music in scheme variables, to be able to use vars with a name verse1. Then I thought about the tree-like structure of music - Reinhold Kainhofer made a nice figure about this in his paper.
2.: I want to use normal music-functions for templates, so anything that fits in a music template, fits in a template. The JSON-like template syntax for oly is not my taste ;-)

The music is stored in a hash-tree-structure, wich uses a goops-class "tree" (found in lascm.scm):
--snip--
; ...
(define-class <tree> ()
  (children #:accessor children #:init-thunk make-hash-table)
  (key #:accessor key #:init-keyword #:key #:init-value 'node)
  (value #:accessor value #:setter set-value! #:init-value #f)
)

(define-method (tree-set! (tree <tree>) (path <list>) val)
  (if (= (length path) 0)
      (set! (value tree) val)
      (let* ((ckey (car path))
             (cpath (cdr path))
             (child (hash-ref (children tree) ckey))
            )
            (if (not (is-a? child <tree>))
                (begin (set! child (make <tree> #:key ckey))
                       (hash-set! (children tree) ckey child)
            ))
            (tree-set! child cpath val)
  ))
  val)
; ...
(define-method (tree-get-tree (tree <tree>) (path <list>))
  (if (= (length path) 0)
      tree
      (let* ((ckey (car path))
             (cpath (cdr path))
             (child (hash-ref (children tree) ckey))
            )
            (if (is-a? child <tree>)
                (tree-get-tree child cpath)
                #f)
)))
(define-method (tree-get (tree <tree>) (path <list>))
  (let ((ctree (tree-get-tree tree path)))
       (if (is-a? ctree <tree>) (value ctree) #f)))
; ...
--snip--
Hidden in a module '(lalily store) is a tree, to which music is stored with the \putMusic command. Also there is a "current music folder" var - I call it folder, because the term context is already reserved in lily, and this tree forms a file-system-like structure: \getMusic resolves the music stored in the tree in the given path relative to the current folder.
A template is music-function with a defined signature also stored in a tree:
\registerTemplate #'(generic) #(define-music-function (parser location piece options)(list? list?)
  ; location parameter for error messages, if music is not found
  (get-music piece location))

This tree-like structure allows several music pieces side by side. A collection of songs might lead to a tree:
chor/alta-trinita/music/sop
chor/alta-trinita/text/sop
chor/alta-trinita/music/alt
chor/alta-trinita/text/alt
...
chor/horianna/music/sop
chor/hosianna/text/sop
...

Then, when calling a template for satb-choral music, you give paths '(chor alta-trinita) and '(chor hosianna). This will fetch the music from the store as needed.
If a template is called with \callTemplate, the current folder and the current template are set accordingly on a path-stack and reset when the template-function returns.
So writing a template means writing a scheme-function. That is not simplifying input in the first place. And storing music means using a scheme-list as a path. This also is not simplifying the input - in the first shot.
But it divides input in storing music and calling a template. This is an advantage for automation and it simplifies input, if one can input scheme-values, which are used in several places in lily (e.g. pairs in markup-overrides).
And a template can loop over the music - I use this in the "choral-lied" templates to create a Lyrics-context for each verse. Writing a template may get confusing, but if a template needed is at hand, typesetting music can be reduced almost to input the notes.
For a music folder, a template to use can be stored. A template can call another template relative to its own path, so if you have a template '(lead-sheet), it might recursively call a template '(lead-sheet lyrics).
I also implemented some functions to automate the creation of the actual scores. The one I use most times is \lalilyTest, wich creates a bookpart with \layout- and \midi-block only if parser-output matches the file referenced in location. So Output is only generated if I compile the file directly. If this file is included in another file, the "music store" is filled, but no output is generated. So to test the input, I work directly on this file, to generate a publication, I might use another file, including all music needed and generating scores and bookparts without midi.
There may also be more informations stored for a specific 'music-folder' like title, layout or paper, which will be used, when generating a score or bookpart.

My template-collection is not a complete set, but it can be easily extented for your own needs, if you are able to define a music-function.

Generally speaking, i'd very much linke to simplify writing LilyPond
scores.  Have you read my article "LilyPond's future"?
(http://news.lilynet.net/?The-LilyPond-Report-25&lang=en#lilypond_s_future)
...
\new Staff = "tenor" { \music }

would be equivalent to:

\new Staff = "tenor" \with {
   \consists Ambitus_engraver
} {
   \clef "G_8"
   \set Staff.instrumentName = "Tenor"
   \set Staff.shortInstrumentName = "T"
   \set Staff.midiInstrument = "Choir Aahs"
   \autoBeamOff
   \music
}
...

I think, you will need one or two infos more, to distinguish the multiple appearances of tenors.
But if you have a template #'(choral tenor), you can call it and call it accordingly.
--snip--
\version "2.15.36"
\include "lalily.ly"

% if you have a template (this is not a simplification)
\registerTemplate #'(choral tenor) #(define-music-function (parser location piece options)(list? list?)
  #{
    <<
      \new Staff = "tenor" \with {
        \consists Ambitus_engraver
      } {
        \clef "G_8"
        \set Staff.instrumentName = "Tenor"
        \set Staff.shortInstrumentName = "T"
        \set Staff.midiInstrument = "Choir Aahs"
        \autoBeamOff \dynamicUp
        \new Voice = "tenor" \getMusic #'(music tenor)
      }
      \new Lyrics \lyricsto "tenor" \getMusic #'(text tenor)
    >>
#})

% you can use it in these few lines (I think this is a simplification, if one looses the fear regarding '#')
\setDefaultTemplate #'(my music) #'(choral tenor) #'()
\setTitle "My Music"

\putMusic #'(music tenor) \relative c' {
  bes4 a c b
}
\putMusic #'(text tenor) \lyricmode {
  B A C H
}

\lalilyTest
--snip--

The template might be found in either a file templates./myown/.ly in the lalily folder of this bundle or a file lalily-templates.ly in the current directory.

In the current templates.ly file are found:
#'(generic) returns the music found at the current path
#'(NOTFOUND) like generic, but also outputs a warning - used, when template not found
#'(transpose) transpose the music, returned by named template - used by \setTransposedTemplate pitch-from pitch-to #'(music path) #'(template path) #'(options)

#'(lead-sheet) create lead-sheet uses: #'(meta) #'(chords) #'(global) #'(melody) #'(lyrics /verse/)
#'(lead-sheet lyrics) called by #'(lead-sheet)

#'(choral group) create a choir staff with staffs named in options (default is satb)
#'(choral group staff lyrics) create one choral staff with lyrics - called by #'(choral group)
    These expect music #'(noten voc) and #'(text voc)
    The structure of the piece is defined in #'(meta)
    Before each voice #'(global) is included

#'(choral lied staff) staff part
#'(choral lied staff lyrics) lyrics part
#'(choral lied satb4) layout music in 4 staves
#'(choral lied satb2) layout music in 2 staves with lyrics in the middle
#'(choral lied satb2b) layout music in 2 staves with lyrics under each stave
    These #'(choral lied) templates look for #'(noten /sop|alt|ten|bas/) and #'(text /verse/)

The options given need to be a list and most times are an a-list.
For example here a transposed one:
--snip--
\version "2.15.36"
\include "lalily.ly"

% options in an a-list, containing an a-list
\setTransposedTemplate c' bes #'(my music) #'(choral group) #'((staffs . (
   (sop . ((inst . "Sopran")
           (sinst . "S")))
   (alt . ((inst . "Alt")
           (sinst . "A")))
   (ten . ((clef . "G_8")
           (inst . "Tenor")
           (sinst . "T")))
   (bas . ((clef . "bass")
           (inst . "Bass")
           (sinst . "B")))
)))

\putMusic #'(meta) { \key c \major \time 4/4 s1 \override StaffGroup.SpanBar #'transparent = ##f \bar "|." }
\putMusic #'(global) { \autoBeamOff \dynamicUp }

\putMusic #'(noten sop) \relative c'' { c4 }
\putMusic #'(text sop) \lyricmode { do }
\putMusic #'(noten alt) \relative c'' { g4 }
\putMusic #'(text alt) \lyricmode { di }
\putMusic #'(noten ten) \relative c' { e4 }
\putMusic #'(text ten) \lyricmode { da }
\putMusic #'(noten bas) \relative c { c4 }
\putMusic #'(text bas) \lyricmode { dum }

\lalilyTest
--snip--
To make the options not to weird ... an a-list containing a-lists ... not nice and not *easy* ...
there is a command \setOption:
\setOption #'staffs #'(
   (sop . ((inst . "Sopran")
           (sinst . "S")))
   (alt . ((inst . "Alt")
           (sinst . "A")))
   (ten . ((clef . "G_8")
           (inst . "Tenor")
           (sinst . "T")))
   (bas . ((clef . "bass")
           (inst . "Bass")
           (sinst . "B")))
)

Still there is list-thing to write - that is not nice. So I will probably rewrite the command \setOption #'(/path/)

Ok, enough for now ... so much to say about this, but only, if there is anyone interested (Janek, you have to do your exams and GSoC  - so I don't expect you digging deep ;-) )

Cheers, Jan-Peter


reply via email to

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