3

My current project is to create a number of short snippets for doumbek. To create each snippet, I use the following \score block:

\header { piece = "Name here" }  % different for each snippet
\score {
  \new DrumStaff \with {
    instrumentName = "doumbek"
    drumStyleTable = #timbales-style
    \override StaffSymbol.line-count = #2
    \override BarLine.bar-extent = #'(-1 . 1)
  } \drummode {
    \time 2/4  % music goes here
    timl8.-> ssh16 timl8-> ssl->  % the actual music
    \bar ":|." 
  }
}
\markup { "Additional performance notes here" }  % only if needed

The complications with typing this are:

  1. Copying all that for each snippet is unacceptably duplicative.
  2. There are about 40 of these two-bar snippets in the project.
  3. It's all the customization of the DrumStaff that adds the majority of the bloat.

After searching (and not finding) how exactly Lilypond internally defines \drums as \new DrumStaff \drummode (so I could define some kind of \DoumbekStaff), I found a way to define a Scheme function to generate the \score blocks I want.

My code now looks like:

% define the Scheme \score template
dscore =
#(define-scheme-function
  (title ts perf-notes music)
  (markup? fraction? markup? ly:music?)
  #{
    \header { piece = $title }
    \score {
      \new DrumStaff \with {
        instrumentName = "doumbek"
        drumStyleTable = #timbales-style
        \override StaffSymbol.line-count = #2
        \override BarLine.bar-extent = #'(-1 . 1)
      } \drummode {
        \time $ts
        $music 
        \bar ":|." 
      }
    }
    \markup { $perf-notes }
  #})

% the actual music
#(dscore 
  "Ayoub" 
  (2 . 4) 
  ""
  #{
    \repeat percent 3 { timl8.-> ssh16 timl8-> ssl-> } |
    timl16-> ssh ssl-> ssh timl-> ssh ssl8-> |
  #}
)

Running this as written simply doesn't work. Guile signals an error for the expression beginning with #(dscore because "Wrong number of arguments to 2". Changing the time signature from 2/4 to 5/4 for testing changes the error to "Wrong number of arguments to 5", so the current problem is with the time signature. fraction? is supposed to be a fraction represented by a pair but neither my (2 . 4) nor my (5 . 4) has been accepted.

Quoting the pair '(2 . 4) makes that error go away but then I am assaulted with numerous complaints that my \drummode notes are not, in fact, notes. It seems to attempt to evaluate the music expression before expanding the dscore function and entering the \drummmode that is in the function.

Commenting out the drum music and replacing it with normal note name notation, like a2 b4 c, to see if it works at all gives me a fatal error of "unexpected \score, expecting end of input".

Finally, there are almost certainly numerous other mistakes in the above code. Although the time signature being uncooperative is my current blocking issue, pointers about other mistakes I made would be much appreciated, as I cannot make any further progress until this template works.

1 Answer 1

3

First a little remark: You can call the scheme function directly by \dscore. Then you need to enter your music in drummode, so you need to use \drummode in the argument instead of the score. Then your scheme function cannot return multiple objects, such as a score and a markup. Instead you should directly register these objects from your scheme function. Also the score header should go into the \score block, not in front of it.

This is a corrected version of your example:

% define the Scheme \score template
dscore =
#(define-scheme-function
  (title ts perf-notes music)
  (markup? fraction? markup? ly:music?)
  (let ((score 
         #{
            \score {
              \header { piece = $title }
              \new DrumStaff \with {
                instrumentName = "doumbek"
                drumStyleTable = #timbales-style
                \override StaffSymbol.line-count = #2
                \override BarLine.bar-extent = #'(-1 . 1)
              } {
                \time $ts
                $music 
                \bar ":|." 
              }
            }
         #}))
    (add-score score)
    (add-text perf-notes)))
    

% the actual music
\dscore
  "Ayoub" 
  #'(2 . 4) 
  "test test test"
  \drummode {
    \repeat percent 3 { timl8.-> ssh16 timl8-> ssl-> } |
    timl16-> ssh ssl-> ssh timl-> ssh ssl8-> |
  }

EDIT: Addressing your comments this is a version that allows creation of different staves, including some optional arguments for Staff type, line count and bar height.

\version "2.22"

% define the Scheme \score template

#(define (music-has-drums? m)
   (if (ly:music? m)
       (if (not (null? (ly:music-property m 'drum-type))) #t
           (or (music-has-drums? (ly:music-property m 'element))
               (any music-has-drums? (ly:music-property m 'elements))))
       #f))

dscore =
#(define-scheme-function
  (title staff-type line-count bar-line-height ts perf-notes music)
  (markup? (symbol? 'default) (number? -1) (number? -1) fraction? markup? ly:music?)
  (let* ((has-drums? (music-has-drums? music))
         (actual-staff-type
          (if (equal? staff-type 'default)
              (if has-drums? 'DrumStaff 'Staff)
              staff-type))
         (actual-line-count
          (if (= line-count -1)
              (case actual-staff-type
                ((DrumStaff) 2)
                (else 5))
              line-count))
         (actual-bar-line-height
          (if (= bar-line-height -1)
              (case actual-staff-type
                ((DrumStaff) 2)
                ((TabStaff) 7.5)
                (else 4))
              bar-line-height))
         (bar-extent (if (null? actual-bar-line-height)
                         '()
                         (cons (- (/ actual-bar-line-height 2)) (/ actual-bar-line-height 2))))
         (score 
          #{
             \score {
               \header { piece = $title }
               \new #actual-staff-type \with {
                 instrumentName = "doumbek"
                 drumStyleTable = #timbales-style
                 \override StaffSymbol.line-count = #actual-line-count
                 \override BarLine.bar-extent = #bar-extent
               } {
                 \time $ts
                 $music 
                 \bar ":|."
               }
             }
          #}))
    (add-score score)
    (add-text perf-notes)))
    

% the actual music

%automatic with drums
\dscore
  "Ayoub" 
  2/4
  "test test test"
  \drummode {
    \repeat percent 3 { timl8.-> ssh16 timl8-> ssl-> } |
    timl16-> ssh ssl-> ssh timl-> ssh ssl8-> |
  }

%automatic with normal music
\dscore
  "Ayoub"
  2/4
  "test test test"
  {
    c d e f
  }

%automatic with 3 bar lines
\dscore
  "Ayoub" 
  default 3 3
  2/4
  "test test test"
  \drummode {
    \repeat percent 3 { timl8.-> ssh16 timl8-> ssl-> } |
    timl16-> ssh ssl-> ssh timl-> ssh ssl8-> |
  }
  
% TabStaff
\dscore
  "Ayoub"
  TabStaff
  2/4
  "test test test"
  {
    c d e f
  }
4
  • Will I need to enter \drummode for each snippet, then?
    – cjm
    Commented Jan 15, 2022 at 12:21
  • 2
    @cjm Yes. ´\drummode´ is a keyword reserved by the parser that tells the parser to interpret the argument as drums. So without it Lilypond would try to interpret the music as regular music and will fail as timl is not a valid note name.
    – Lazy
    Commented Jan 15, 2022 at 12:57
  • With \drummode in each snippet, it also means that I could add normal melodic snippets if I wanted while using the same macro. I just tested your revision to my function with multiple snippets and it works like a charm.
    – cjm
    Commented Jan 15, 2022 at 18:55
  • @cjm That is indeed possible but will need additional work. See the edit to my answer.
    – Lazy
    Commented Jan 15, 2022 at 23:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.