Printing music scores

Han Oostdijk


Date last run: 01Nov2019


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 (<>).
'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 of tabr and version 2.19.83 of LilyPond.


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:

As an example I want to print the normal range on a guitar (tabr is specially useful for fretted instruments).


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

tab(song, glue::glue("{filename}.{filetype}"), key="c", time="4/4",

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,

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) {
    glue::glue("lilypond.exe --{filetype} -dstrip-output-dir=#f \"{filepref}\"") ,    
    show.output.on.console = details

filename_out = glue::glue("{filename}_v2")
# render the adapted file

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