This section covers lilypond()
and tab()
for writing LilyPond files and engraving to sheet music with LilyPond,
respectively. Examples of the various pieces of metadata that can be
passed to to these functions are provided.
The lilypond()
function creates a LilyPond file
(.ly
). Various rendering functions wrap
lilypond()
to combine the steps of creating an intermediary
LilyPond file and rendering it to pdf or png. The most general render
function is tab()
, which for naming consistency can also be
called as render_tab()
. Other render_*
functions exist including render_score()
and
render_midi()
, which focus on rendering non-tablature,
all-purpose sheet music and rendering only the corresponding MIDI file,
respectively.
The examples in this vignette focus on tab()
. You can
set keep_ly = TRUE
in render functions to retain the
intermediary LilyPond file.
By now you have seen many calls to tab()
throughout
these vignettes in order to show full examples previously. With
sufficient coverage of phrases, tracks and scores, and the progression
through them, now it is time to go into more detail on the arguments
that can be supplied as part of the rendering or engraving process that
apply to the entire piece of sheet music.
These are the most critical components that can be supplied to
lilypond()
. key
specifies the global key
signature, e.g. key ="dm"
. Key signatures set for
individual track override the global key, but the global key is used for
any track whose key is NA
. Key changes in the middle of a
song are not supported, but you can always edit the LilyPond file
directly to make custom changes.
time
gives the time signature, defaulting to common
time, "4/4"
. tempo
provides the song tempo,
defaulting to tempo = "2 = 60"
, which is the LilyPond
default. This can be read as 60 half note beats per minute.
Note that key
takes the tabr
key signature
notation, but time
and tempo
which are used
mostly in a transcription context like this, take values that match
LilyPond format.
Throughout this tutorial section, the guitar and bass example from the section on tracks and scores is reused. The chord chart and chord sequence are retained to provide the most complete illustration of a rendered score. For completeness, here is what you have so far.
voice1 <- rp(p("c5 d5 e5 f5 g5", "1 2 4 4 1", "1*5"), 2)
notes <- "c e g c' e' c' g e g b d' g' f a c' f' c e g e c"
strings <- "5 4 3 2 1 2 3 4 4 3 2 1 4 3 2 1 5 4 3 4 5"
voice2 <- rp(p(notes, "8*20 2", strings), 2)
bass <- rp(p("c2e2*4 g1*2 f1*2 c2e2*3", "4*10 2", "32*4 4*4 32*3"), 2)
t1 <- track(voice1, voice = 1)
t2 <- track(voice2, voice = 2)
t3 <- track(bass, clef = "bass_8", tuning = "bass")
chords <- chord_set(c(c = "x32o1o", g = "355433", f = "133211"))
chord_seq <- rep(setNames(c(1, 2, 2, 1), names(chords)[c(1:3, 1)]), 3)
chords
#> c g f
#> "x;3;2;o;1;o;" "3;5;5;4;3;3;" "1;3;3;2;1;1;"
chord_seq
#> c g f c c g f c c g f c
#> 1 2 2 1 1 2 2 1 1 2 2 1
#> # A tibble: 3 × 8
#> phrase clef key tab tuning voice lyrics id
#> <list> <chr> <chr> <lgl> <chr> <int> <chr> <int>
#> 1 <phrase [1]> treble_8 NA TRUE e,a,dgbe' 1 NA 1
#> 2 <phrase [1]> treble_8 NA TRUE e,a,dgbe' 2 NA 1
#> 3 <phrase [1]> bass_8 NA TRUE e,,a,,d,g, 1 NA 2
This time when rendering the song, use settings for the three
arguments discussed above. Given what is written, it doesn’t make sense
to change them all. For example, this won’t fit well as a waltz
(time = "3/4"
). But for illustration purposes, pretend the
song is actually in the key of D minor (F major). This key has one flat,
Bb, so this will affect the display of the B note in the G chord. Change
the time to 2/2
just for the sake of changing it, which
won’t really be any different from 4/4
except you will see
a line through the common time symbol. Finally, change the tempo to
4 = 120
, which is also equivalent to the default, but will
show up in the output slightly differently as well.
Notice how the key change alone, which added room for a single flat symbol to be squeezed into the start of the treble and bass clef staves, was enough to a line wrap compared to the previous tutorial section where it all fit on one line. This is because it was a tight fit to in the previous examples and there was not enough room for LilyPond to continue fitting everything on one line with this slight widening created by the key change. LilyPond generates sheet music with a somewhat responsive layout engraver. It won’t necessarily leave everything on one line and push only a single measure to line two.
The next important argument to lilypond()
is
header
. header
takes a named list of character
strings that are used to fill in general song information such as the
title and composer. All previous examples were blank above the tabs
because none of this information was ever provided. Below an example is
given that uses all available arguments. You can use any subset of
these. There is no requirement to supply them all. This example just
shows what is available. Several of these would likely not be used in
most cases.
header <- list(
title = "Song title",
composer = "Words and music by the composer",
performer = "Song performer",
album = "Album title",
subtitle = "Subtitle",
arranger = "Arranged by tab arranger",
copyright = "2018 <Record Label>",
instrument = "guitar and bass",
tagline = "A tagline",
meter = "meter tag", opus = "opus tag", piece = "piece tag", poet = "poet tag"
)
tab(song, "ex33.pdf", header = header)
The copyright and tagline are cutoff in the above image, but in your pdf you will see these if you scroll to the bottom of the rendered page.
You have probably noticed by now that every time a pdf if rendered,
an accompanying MIDI file is also generated. This can be turned off with
midi = FALSE
. This specification goes into the LilyPond
file itself via lilypond()
. Therefore, no MIDI output will
be created even if you create the LilyPond file with
lilypond()
but convert that to pdf with LilyPond outside of
R.
One thing to note about MIDI files is that tabr
will
unfold any repeats that occur in a song due to calls to
rp()
, pct()
or volta()
. This
allows the MIDI file to play everything the proper number of times
rather than ending prematurely by not being able to read repeat
notation.
Also, tabr
is a package aimed at creating guitar
tablature. It is not concerned with MIDI or audio signals and audio data
in general. Any MIDI functionality is considered to be an extra feature
and does not receive priority development or support. At this time,
there is no way to make other alterations to the MIDI file internals.
MIDI output can be toggled on or off as mentioned. And without you
having to do anything, MIDI output will respect repeat notation in the
rendered LilyPond file. MIDI output will also automatically be
transposed to match a transposition that is applied to a music staff
(under reasonable and simplified conditions).
The string_names
is only relevant for tablature staves.
argument defaults to NULL
. This means that standard tuning
is never specified alongside the lines of a tab staff at the beginning
of the tablature. However, any other tuning will be explicitly noted so
the reader is aware of the alternate tuning of any applicable track.
This can also be set to TRUE
to force all tunings to be
explicit including standard guitar tuning or FALSE
to
suppress them all (though it is unclear what value there is in the
FALSE
setting).
There is some nominal level of control over the paper layout via
paper
. Like header
, this is a named list. The
defaults are fine so you probably do not need to alter most of these
values. There are six options, all numeric except for
page_numbers
, which is logical.
textheight
linewidth
indent
first_page_number
page_numbers
fontsize
If you pass any values for some of these in a named list to
paper
, any not passed will retain their built in defaults.
You do not have to supply them all when using paper
.
There are also global color settings. You can pass a named list to
colors
with any of the following. Values are hex colors or
R color names.
color
background
staff_lines
time
clef
beam
head
stem
accidental
For more detail on these settings and others further above, see the
help documentation for lilypond()
.
Here are examples that make use of png-specific options and function behavior. First, when rendering to png instead of pdf, automatic cropping of the image is attempted. This removes the full page appearance for smaller snippets of sheet music. There are some limitations to this. For example, inclusion of header elements can disrupt this behavior.
x <- pitch_seq("e,", 24, key ="c") |> as_music(8) |> p() |> track() |> score()
ppr <- list(linewidth = 60, page_numbers = FALSE)
hdr <- list(title = "Notes in C on guitar", subtitle = "Standard tuning")
tab(x, "ex_png1.png", tempo = NULL, midi = FALSE, header = hdr, paper = ppr)
Above, the height is automatically cropped because file
type is a png. Providing textheight
to paper
overrides this behavior. The width would have also been cropped on its
own. However, because of the inclusion of header
arguments,
this fails and explicit linewidth
is needed. Similarly, if
elements that describe footer content (still part of the LilyPond
header
block) that appear at the bottom of the page such as
tagline
or copyright
were included, automatic
height cropping would have also failed. In general, automatic cropping
is the default for png output, but header
disrupts this,
requiring manual overrides to width and height.
In the example below, remove the header content. You can make a transparent background png, though you won’t be able to see the difference here. Instead, create a dark mode example and increase the resolution.
Since this is a single voice, single track, you do not need to
explicitly call each function in the transcription pipeline. Use one of
the convenient render_music*
functions to abstract this
process, rendering sheet music directly from a music
object. render_music_guitar()
has good default arguments
for this example.
colors <- list(color = "#e4e4e4", background = "gray10", head = "tomato", tabhead = "tomato")
x <- as_music(pitch_seq("e,", 24, key ="c"), 8) |>
render_music_guitar("ex_png2.png", colors = colors, res = 300)
Finally, familiarize yourself with the LilyPond file itself. You may
output a file and want to make additional edits to it directly. The
example below also demonstrates some differences between
simplify = TRUE
and simplify = FALSE
. This is
a simple piece of music so not all possible syntax simplifications are
shown here.
phrase()
objects have more robust structure that has
advantages for performing object manipulations in R, but this is a more
verbose version of LilyPond syntax. By default, lilypond()
as well as associated render_*
functions will simplify this
to a more efficient syntax so that it is less cumbersome to read and
work with LilyPond files directly.
There are other simplifications but the most notable benefits are the
removal of all the <>
around single notes and not
repeating consecutive durations if they are unchanged from previous
timesteps.
#> \version "2.23.6"
#> #(ly:set-option 'crop #t)\paper{
#> line-width=150\mm
#> oddFooterMarkup = ##f
#> oddHeaderMarkup = ##f
#> bookTitleMarkup = ##f
#> scoreTitleMarkup = ##f
#> indent = 0.\mm
#> first-page-number = 1
#> print-page-number = ##t
#> print-first-page-number = ##t
#> }
#>
#> #(set-global-staff-size 10)
#> \header {
#> title = ""
#> subtitle = ""
#> composer = ""
#> arranger = ""
#> instrument = ""
#> metre = ""
#> opus = ""
#> piece = ""
#> poet = ""
#> copyright = ""
#> tagline = ""
#> }
#> \include "predefined-guitar-fretboards.ly"
#>
#> #(define fb1 (make-fretboard-table))
#> \storePredefinedDiagram #fb1 \chordmode{c} #guitar-tuning "x;3;2;o;1;o;"
#> #(define fb2 (make-fretboard-table))
#> \storePredefinedDiagram #fb2 \chordmode{g} #guitar-tuning "3;5;5;4;3;3;"
#> #(define fb3 (make-fretboard-table))
#> \storePredefinedDiagram #fb3 \chordmode{f} #guitar-tuning "1;3;3;2;1;1;"
#>
#> global = {
#> \time 4/4
#> \tempo 2 = 60
#> \bar "|."
#> }
#>
#> global_key = {
#> \key c \major
#> }
#>
#> mychorddiagrams = \chordmode {
#> \set predefinedDiagramTable = #fb1 c
#> \set predefinedDiagramTable = #fb2 g
#> \set predefinedDiagramTable = #fb3 f
#> }
#>
#> chordNames = \chordmode {
#> \override ChordName.font-size = #2
#> \global
#> c1 g2 f2 c1 c1 g2 f2 c1 c1 g2 f2 c1
#> }
#>
#> \markup\vspace #3
#> \markup \fill-line {
#> \score {
#> <<
#> \context ChordNames { \mychorddiagrams }
#> \context FretBoards {
#> \override FretBoards.FretBoard.size = #1.2
#> \mychorddiagrams
#> }
#> >>
#> \layout {}
#> }
#> }
#> \markup\vspace #3
#>
#> melodyAA = {
#> \global
#> \override StringNumber.transparent = ##t
#> \voiceOne \repeat unfold 3 { <c''\1>1 <d''\1>2 <e''\1>4 <f''\1>4 <g''\1>1 }
#> }
#>
#> melodyAB = {
#> \global
#> \override StringNumber.transparent = ##t
#> \voiceTwo \repeat unfold 3 { <c\5>8 <e\4>8 <g\3>8 <c'\2>8 <e'\1>8 <c'\2>8 <g\3>8 <e\4>8 <g\4>8 <b\3>8 <d'\2>8 <g'\1>8 <f\4>8 <a\3>8 <c'\2>8 <f'\1>8 <c\5>8 <e\4>8 <g\3>8 <e\4>8 <c\5>2 }
#> }
#>
#> melodyB = {
#> \global
#> \override StringNumber.transparent = ##t
#> \repeat unfold 3 { <c,\3 e,\2>4 <c,\3 e,\2>4 <c,\3 e,\2>4 <c,\3 e,\2>4 <g,,\4>4 <g,,\4>4 <f,,\4>4 <f,,\4>4 <c,\3 e,\2>4 <c,\3 e,\2>4 <c,\3 e,\2>2 }
#> }
#>
#> midimelodyAA = {
#> \global
#> \override StringNumber.transparent = ##t
#> \unfoldRepeats { \voiceOne \repeat unfold 3 { <c''\1>1 <d''\1>2 <e''\1>4 <f''\1>4 <g''\1>1 }
#> }}
#>
#> midimelodyAB = {
#> \global
#> \override StringNumber.transparent = ##t
#> \unfoldRepeats { \voiceTwo \repeat unfold 3 { <c\5>8 <e\4>8 <g\3>8 <c'\2>8 <e'\1>8 <c'\2>8 <g\3>8 <e\4>8 <g\4>8 <b\3>8 <d'\2>8 <g'\1>8 <f\4>8 <a\3>8 <c'\2>8 <f'\1>8 <c\5>8 <e\4>8 <g\3>8 <e\4>8 <c\5>2 }
#> }}
#>
#> midimelodyB = {
#> \global
#> \override StringNumber.transparent = ##t
#> \unfoldRepeats { \repeat unfold 3 { <c,\3 e,\2>4 <c,\3 e,\2>4 <c,\3 e,\2>4 <c,\3 e,\2>4 <g,,\4>4 <g,,\4>4 <f,,\4>4 <f,,\4>4 <c,\3 e,\2>4 <c,\3 e,\2>4 <c,\3 e,\2>2 }
#> }
#> }
#>
#> \score { <<
#> \new ChordNames \chordNames
#> \new Staff << \clef "treble_8" \global_key \context Voice = "melodyAA" \melodyAA \context Voice = "melodyAB" \melodyAB >>
#> \new TabStaff \with { stringTunings = \stringTuning <e, a, d g b e'> } <<
#> \override Stem.transparent = ##t
#> \override Beam.transparent = ##t
#> \context TabVoice = "melodyAA" \melodyAA \context TabVoice = "melodyAB" \melodyAB
#> >>
#> \new Staff << \clef "bass_8" \global_key \melodyB >>
#> \new TabStaff \with { stringTunings = \stringTuning <e,, a,, d, g,> } <<
#> \set TabStaff.instrumentName = \markup { \hspace #7 \override #'(baseline-skip . 1.5) \column \fontsize #-4.5 \sans { G D A E } }
#> \override Stem.transparent = ##t
#> \override Beam.transparent = ##t
#> \melodyB
#> >>
#> >>
#> \layout{ }
#> }
#>
#> \score { <<
#> \midimelodyAA
#> \midimelodyAB
#> \midimelodyB
#> >>
#> \midi{
#> \tempo 2 = 60
#> }
#> }
Here you can see the changes to the notation. Since the music is so short in this example, most of the file remains the same.
#> \version "2.23.6"
#> #(ly:set-option 'crop #t)\paper{
#> line-width=150\mm
#> oddFooterMarkup = ##f
#> oddHeaderMarkup = ##f
#> bookTitleMarkup = ##f
#> scoreTitleMarkup = ##f
#> indent = 0.\mm
#> first-page-number = 1
#> print-page-number = ##t
#> print-first-page-number = ##t
#> }
#>
#> #(set-global-staff-size 10)
#> \header {
#> title = ""
#> subtitle = ""
#> composer = ""
#> arranger = ""
#> instrument = ""
#> metre = ""
#> opus = ""
#> piece = ""
#> poet = ""
#> copyright = ""
#> tagline = ""
#> }
#> \include "predefined-guitar-fretboards.ly"
#>
#> #(define fb1 (make-fretboard-table))
#> \storePredefinedDiagram #fb1 \chordmode{c} #guitar-tuning "x;3;2;o;1;o;"
#> #(define fb2 (make-fretboard-table))
#> \storePredefinedDiagram #fb2 \chordmode{g} #guitar-tuning "3;5;5;4;3;3;"
#> #(define fb3 (make-fretboard-table))
#> \storePredefinedDiagram #fb3 \chordmode{f} #guitar-tuning "1;3;3;2;1;1;"
#>
#> global = {
#> \time 4/4
#> \tempo 2 = 60
#> \bar "|."
#> }
#>
#> global_key = {
#> \key c \major
#> }
#>
#> mychorddiagrams = \chordmode {
#> \set predefinedDiagramTable = #fb1 c
#> \set predefinedDiagramTable = #fb2 g
#> \set predefinedDiagramTable = #fb3 f
#> }
#>
#> chordNames = \chordmode {
#> \override ChordName.font-size = #2
#> \global
#> c1 g2 f2 c1 c1 g2 f2 c1 c1 g2 f2 c1
#> }
#>
#> \markup\vspace #3
#> \markup \fill-line {
#> \score {
#> <<
#> \context ChordNames { \mychorddiagrams }
#> \context FretBoards {
#> \override FretBoards.FretBoard.size = #1.2
#> \mychorddiagrams
#> }
#> >>
#> \layout {}
#> }
#> }
#> \markup\vspace #3
#>
#> melodyAA = {
#> \global
#> \override StringNumber.transparent = ##t
#> \voiceOne \repeat unfold 3 { c''1\1 d''2\1 e''4\1 f''\1 g''1\1 }
#> }
#>
#> melodyAB = {
#> \global
#> \override StringNumber.transparent = ##t
#> \voiceTwo \repeat unfold 3 { c8\5 e\4 g\3 c'\2 e'\1 c'\2 g\3 e\4 g\4 b\3 d'\2 g'\1 f\4 a\3 c'\2 f'\1 c\5 e\4 g\3 e\4 c2\5 }
#> }
#>
#> melodyB = {
#> \global
#> \override StringNumber.transparent = ##t
#> \repeat unfold 3 { <c,\3 e,\2>4 <c,\3 e,\2> <c,\3 e,\2> <c,\3 e,\2> g,,\4 g,,\4 f,,\4 f,,\4 <c,\3 e,\2> <c,\3 e,\2> <c,\3 e,\2>2 }
#> }
#>
#> midimelodyAA = {
#> \global
#> \override StringNumber.transparent = ##t
#> \unfoldRepeats { \voiceOne \repeat unfold 3 { c''1\1 d''2\1 e''4\1 f''\1 g''1\1 }
#> }}
#>
#> midimelodyAB = {
#> \global
#> \override StringNumber.transparent = ##t
#> \unfoldRepeats { \voiceTwo \repeat unfold 3 { c8\5 e\4 g\3 c'\2 e'\1 c'\2 g\3 e\4 g\4 b\3 d'\2 g'\1 f\4 a\3 c'\2 f'\1 c\5 e\4 g\3 e\4 c2\5 }
#> }}
#>
#> midimelodyB = {
#> \global
#> \override StringNumber.transparent = ##t
#> \unfoldRepeats { \repeat unfold 3 { <c,\3 e,\2>4 <c,\3 e,\2> <c,\3 e,\2> <c,\3 e,\2> g,,\4 g,,\4 f,,\4 f,,\4 <c,\3 e,\2> <c,\3 e,\2> <c,\3 e,\2>2 }
#> }
#> }
#>
#> \score { <<
#> \new ChordNames \chordNames
#> \new Staff << \clef "treble_8" \global_key \context Voice = "melodyAA" \melodyAA \context Voice = "melodyAB" \melodyAB >>
#> \new TabStaff \with { stringTunings = \stringTuning <e, a, d g b e'> } <<
#> \override Stem.transparent = ##t
#> \override Beam.transparent = ##t
#> \context TabVoice = "melodyAA" \melodyAA \context TabVoice = "melodyAB" \melodyAB
#> >>
#> \new Staff << \clef "bass_8" \global_key \melodyB >>
#> \new TabStaff \with { stringTunings = \stringTuning <e,, a,, d, g,> } <<
#> \set TabStaff.instrumentName = \markup { \hspace #7 \override #'(baseline-skip . 1.5) \column \fontsize #-4.5 \sans { G D A E } }
#> \override Stem.transparent = ##t
#> \override Beam.transparent = ##t
#> \melodyB
#> >>
#> >>
#> \layout{ }
#> }
#>
#> \score { <<
#> \midimelodyAA
#> \midimelodyAB
#> \midimelodyB
#> >>
#> \midi{
#> \tempo 2 = 60
#> }
#> }