Date last run: 01Nov2019
Introduction
I bought an old copy of the book “Early dances/Alte Tänze/Danses anciennes” in the series “Piano Step by Step” by publishing house Könemann Music in Budapest. Because I play guitar and not piano I want to transcribe the base part (with the F key/clef) to the the G key that I am accustomed to as amateur guitar player. So I sought a way to print the transcribed key in combination with the original G key.
This documents my experiments with the CRAN package tabr to achieve this. From the description file of tabr
:
Provides a music notation syntax and a collection of music programming functions for
generating, manipulating, organizing and analyzing musical information in R.
The music notation framework facilitates creating and analyzing music data in notation form.
The package also provides API wrapper functions for transcribing musical representations in R
into guitar tablature ("tabs") using the 'LilyPond' backend (<http://lilypond.org>).
'LilyPond' is open source music engraving software for generating high quality sheet music
based on markup syntax.
'tabr' generates 'LilyPond' files from R code and can pass them to 'LilyPond' to be rendered
into sheet music pdf files from R.
The tabr
website has a very nice introduction to the package.
In the ‘Use case considerations’ it says
tabr offers a useful but limited LilyPond API and is not intended to access all
LilyPond functionality from R, nor is transcription via the API the entire scope of tabr.
For me tabr
was also a good introduction to LilyPond because it shows how relatively simple R-statements get translated to the Lilypond language.
In this document I use version 0.3.9.9000 of tabr
and
version 2.19.83 of LilyPond.
Workflow
With the tabr package it is possible to use R statements to produce printed output in PDF
or PNG
of a musical composition. The workflow (see the tabr
website for background) is as follows:
- specify voices by indicating the pitch, timing (and optionally the string) of the notes
- specify tracks by indicating which voice(s) belong(s) to it
- specify a score by indicating the tracks that it contains
- optionally convert the score to a
.ly
file (functionlilypond
) - convert the score to a
PDF
orPNG
file (functiontab
that uses the functionlilypond
to create an intermediate.ly
file that is rendered by calling the externallilypond.exe
)
As an example I want to print the normal range on a guitar (tabr
is specially useful for fretted instruments).
library(tabr)
notes1 <- "e2 f2 g2 a2 b2 c3 d3 e3 f3 g3 a3 b3 c4 d4 e4 f4 g4 a4 b4 c5 d5 e5 f5 g5"
times1 <- "4*24"
# strings1 <- "6*3 5*3 4*3 3*2 2*3 1*10" # let lilypond determine this given tuning
voice1 <- p(notes1,times1,string=NULL)
track1 <- track(voice1, voice = 1,tuning="e, a, d g b e'")
song <- trackbind(track1,tabstaff = 1) %>% score()
header <- list(title="The musical scales for guitar",
tagline="'composed' by Han Oostdijk")
paper <- list(first_page_number =4,page_numbers=T,print_first_page_number=F,
textheight = 55,linewidth = 120)
filename = "opus1"
filetype = "png" # "pdf"
With the following statement we produce the file opus1.png
and by specifying keep_ly=T
we ‘keep’ the intermediate file opus1.ly
:
tab(song, glue::glue("{filename}.{filetype}"), key="c", time="4/4",
tempo=NULL,string_names=T,header=header,paper=paper,
midi=F,keep_ly=T,details=F)
If we knew beforehand that we only need the .ly
file we could also have used :
lilypond(song, glue::glue("{filename}.{filetype}"), key="c", time="4/4",
tempo="\" \"",string_names=T,header=header,paper=paper,
midi=F)
The resulting file opus1.png
looks like:
By editing the .ly
file we can adapt the score. As an example I add the version number (this avoids a warning in the log file) and an indication of the pitch of the notes.
create_pitch_lines <- function(notes) {
n2 = strsplit(notes,"")[[1]]
n2 = n2[n2 != " "]
m1 = n2[seq(1,length(n2),2)]
m2 = n2[seq(2,length(n2),2)]
template_line = " \\markup { \\concat { $m1] \\sub{$m2]} } }"
pitch_lines = glue::glue(template_line,.open="$",.close="]")
pitch_lines = c(" \\addlyrics {",pitch_lines, " }")
}
edit_ly_file <- function (filepref_in,filepref_out,notes1,add_pitch=T,add_version=T) {
lines = readLines(glue::glue("{filepref_in}.ly"),warn = F)
if (add_version ==T) {
v = system("lilypond.exe --version",intern=T)
v = stringr::str_extract(v[1] ,"(?<=GNU LilyPond )(.*)$")
lines = c(glue::glue("\\version \"{v}\" "),lines)
}
if (add_pitch ==T) {
w = stringr::str_which(lines,"new Staff")[1]
# insert pitch lines after "new Staff"
pitch = create_pitch_lines(notes1)
lines = c(lines[1:w],pitch,lines[(w+1):length(lines)])
}
writeLines(lines,glue::glue("{filepref_out}.ly"),sep = '\n')
}
render_ly_file <- function (filepref,filetype,details=F) {
invisible(
system(
glue::glue("lilypond.exe --{filetype} -dstrip-output-dir=#f \"{filepref}\"") ,
show.output.on.console = details
)
)
}
filename_out = glue::glue("{filename}_v2")
edit_ly_file(filename,filename_out,notes1,add_pitch=T,add_version=T)
# render the adapted file
render_ly_file(filename_out,filetype,details=T)
Then opus1_v2.png
looks like:
We see that the pitch indications are inserted.
Session Info
This document was produced on 01Nov2019 with the following R environment:
#> R version 3.6.0 (2019-04-26)
#> Platform: x86_64-w64-mingw32/x64 (64-bit)
#> Running under: Windows 10 x64 (build 18362)
#>
#> Matrix products: default
#>
#> locale:
#> [1] LC_COLLATE=English_United States.1252
#> [2] LC_CTYPE=English_United States.1252
#> [3] LC_MONETARY=English_United States.1252
#> [4] LC_NUMERIC=C
#> [5] LC_TIME=English_United States.1252
#>
#> attached base packages:
#> [1] stats graphics grDevices utils datasets methods base
#>
#> other attached packages:
#> [1] tabr_0.3.9.9000
#>
#> loaded via a namespace (and not attached):
#> [1] Rcpp_1.0.2 crayon_1.3.4 assertthat_0.2.1 digest_0.6.22
#> [5] HOQCutil_0.1.14 dplyr_0.8.3 R6_2.4.0 magrittr_1.5
#> [9] evaluate_0.14 pillar_1.4.2 rlang_0.4.1 stringi_1.4.3
#> [13] rmarkdown_1.16 tools_3.6.0 stringr_1.4.0 glue_1.3.1
#> [17] purrr_0.3.3 xfun_0.8 compiler_3.6.0 pkgconfig_2.0.3
#> [21] htmltools_0.3.6 tidyselect_0.2.5 knitr_1.25 tibble_2.1.3