Date last run: 13Jan2020
Introduction
In an earlier blog entry I suggested to allow an ‘insert/anchor/hook point’ in the notes in tabr that would make it easier to indicate where changes have to be made. In his GitHub answer Matt Leonawicz liked the general idea, but he was a little worried about the impact it could have on the non-LilyPond related functions and about the time that it would take to implement this. Following his suggestion
I do want to keep the “noise” of LilyPond/transcription-specific use cases isolated to the phrase-track-score-render_* pipeline of functions if possible.
I forked the package and adapted the phrase function. I will send Matt a PR request so that he can have a look at it and come up with improvements and suggestions. The remainder of this blog entry handles my experiences and gives a example of use.
Update 13JAN2020
Matt Leonawicz has two types of objections to the proposed solution:
- it does not fit into the general flow because of the change I made to
is_note. E.g.is_note("a ^1 b ^2 c")would indicate 5 valid notes.
This could be avoided by creating anis_noteversion internal tophrasethat accepts a^. - he sees little use for the package producing Lilypond input and it is always possible to directly write a Lilypond input file.
I will try to include this functionality in some way in the tabraux package.
Experiences and remarks
- I decided to use the
^as the insert hook. Also^1,^2etc. can be used. - I got it working for the simple things I work with. See the last section of
test_phrase.Rfor the cases that were tested. - the
is_notefunction and the.phrasefunction also had to be changed - the
as_musicfunction also builds ‘phrases’ but not via thephrasefunction. I don’t know why a separate path is taken for this. One more reason to not touch this. So the insert hook will not work in ‘music’ and therefore in the following example the first track is not built from ‘music’ as was done before. - the
as_musicfunction supports theaccidentalsargument. I did not find another way to specify this. In the example I use this as an opportunity to show how the hook can be used. - the functions
pcandpnnow handle redundant whitespaces but this is not yet the case for thephrase(p) function. - in the package no changes were made to version numbering and documentation
- a hook is considered in
phraseas new ‘note’. The rule that the number of items ininfoandstringshould match the number ofnotesis still in force. So wheninfoandstringwere specified before inserting the^, then after insertion an extrainfo(e.g.1) andstring(e.g.x) should be coded. This extra information is ignored. I found no easy solution to avoid this.
Example
The example is the same as in the previous blog entry. The difference is that in track1 I use the hook character to (later) insert the bar end. In the second part I show that the ‘ges’ notes can be changed in ‘fis’ notes by using the hooks.
Code:
library(tabr) # in HOQC1 branch
library(tabraux)
to_music <- function (notes,info,key="c",time="3/4",accidentals=NULL) {
notes <- tabraux::expand_notes(notes)
info <- tabraux::check_times(info,steps=4)$times
as_music(notes, info,key=key,time=time,accidentals=accidentals)
}
notes <- paste("e3 f g | g g a b | c4 c b3 | b | c4 d e | d c b3 | a f# g | g f# g ^ |",
"d g f | e f g| r g d e f g | a f g | f g a b | c4 c b_3 | a g f | e g a |",
"b_ f g | a | b g c4 | c b3 c4" )
info <- paste("4*3 | 4. 8*3 | 4 4. 8 | 2. | 4*3 | 4. 8 4 | 4*3 | 4. 8 4 1 | 4 4. 8 | 4. 8 4 |",
"8*6 | 4. 8 4 |","4. 8*3 | 2( 8) 8 | 4*3 | 4. 8 4 | 4. 8 4 | 2. | 4. 8 4 | 4. 8 4")
notes <- tabraux::expand_notes(notes)
info <- tabraux::check_times(info,steps=4)$times
# as_music does not support the hook character!
track1 <- track(p(notes,info,bar= ":|."),
key = "c",voice = 1,tab=F)
track1 <- tabraux::edit_phrase(track1,"\\^", " \\\\bar \":|.|:\" ",all=F)
notes <- pc(pn("s | ",3)," e3 |",pn("s | ",7)," r r f e | ",
pn("s | ",3),"e | d | d c d | d r g | f e |")
info <- pc(pn("2. | ",3)," 2. |",pn("2. | ",7)," 4 8 4 8 | ",
pn("2. | ",3),"2. | 2.( | 4.) 8 4( | 4) 8 4. | 2 4 |")
v2 <- to_music(notes, info,key="c",time="3/4")
track2 <- track(p(music_notes(v2),music_info(v2)),
key = music_key(v2),voice = 2,tab=F)
notes <- paste("g3 f e | r g | r e ae | r g | a b c4 | b3 c4 d | c b3 | a g | r d g |",
"g f e f | g g d e | f g a | a g f | r g a b c4 | c4 b_3 a | g e f |",
"g d e | r f | f f e | d c" )
info <- paste("4*3 | 4 2 | 8*2 2 | 4 2 | 4*3 | 4*3| 2 4 | 2 4 | 4*2 4( | 8) 8 4. 8 |",
" 4. 8*3 | 8*2 2 | 8*2 2 | 8*4 4( | 4) 4*2 | 4*3 | 4*3 | 4 2 | 4*3 | 2 4")
v3 <- to_music(notes, info,key="c",time="3/4")
track3 <- track(p(music_notes(v3),music_info(v3)),
key = music_key(v3),voice = 1,tab=F)
notes <- paste("c3 | b2d3 | a2 | e3 d |c | g | e d | d g2 | b | c3 | b_2 | a | d3 |",
"e | f | c | b_2 | f | g | g c3 |")
info <- paste("2. | 2. | 2. | 2 4 | 2. | 2. | 4 2 | 2 4 | 2. | 2. | 2. | 2. | 2. |",
" 2. | 2. | 2. | 2. | 2. | 2. | 2 4")
v4 <- to_music(notes, info,key="c",time="3/4")
track4 <- track(p(music_notes(v4),music_info(v4)),
key = music_key(v4),voice = 2,tab=F)
song <- trackbind(track1,track2,track3,track4,id=c(1,1,2,2)) %>% score()
v = tabraux::lilypond_version()
t1 = "Adapted from Early Dances (Koeneman Music Budapest) p28."
tagline= glue::glue("{t1} Engraving by LilyPond (version {v}).")
header <- list(piece="Sarabande",
opus="Louis Couperin",
tagline= tagline)
paper <- list(first_page_number =1,page_numbers=T,print_first_page_number=F,
textheight = 50,linewidth = 120)
filename = "sarabande2"
filetype = "png"
With the following statement we produce the file sarabande2.png :
tab(song, glue::glue("{filename}.{filetype}"),
key=music_key(v2), time=music_time(v2), tempo=NULL,
string_names=T,header=header,paper=paper,
midi=F,keep_ly=F,details=F)
The resulting file sarabande2.png looks like:

I don’t like the ges notes that are produced in this sheet (in measures 7 and 8). When I worked with the as_music function to produce track1 I could indicate that I want accidentals to be sharps and here it apparently produces flats. There must be a way to specify this, but I could not find it. Therefore I will overwrite the two occurrences by the corresponding ‘fis’. This is done by replacing the two ‘f#’ in notes by resp. ‘^1’ and ‘^2’ and replacing these by the tabraux::edit_phrase function:
notes <- paste("e3 f g | g g a b | c4 c b3 | b | c4 d e | d c b3 | a ^1 g | g ^2 g ^ |",
"d g f | e f g| r g d e f g | a f g | f g a b | c4 c b_3 | a g f | e g a |",
"b_ f g | a | b g c4 | c b3 c4" )
info <- paste("4*3 | 4. 8*3 | 4 4. 8 | 2. | 4*3 | 4. 8 4 | 4*3 | 4. 8 4 1 | 4 4. 8 | 4. 8 4 |",
"8*6 | 4. 8 4 |","4. 8*3 | 2( 8) 8 | 4*3 | 4. 8 4 | 4. 8 4 | 2. | 4. 8 4 | 4. 8 4")
notes <- tabraux::expand_notes(notes)
info <- tabraux::check_times(info,steps=4)$times
track1a <- track(p(notes,info,bar= ":|."),
key = "c",voice = 1,tab=F)
track1a <- tabraux::edit_phrase(track1a,"\\^1", "<fis>4",all=F)
track1a <- tabraux::edit_phrase(track1a,"\\^2", "<fis>8",all=F)
track1a <- tabraux::edit_phrase(track1a,"\\^", " \\\\bar \":|.|:\" ",all=F)
song <- trackbind(track1a,track2,track3,track4,id=c(1,1,2,2)) %>% score()
filename = "sarabande2a"
tab(song, glue::glue("{filename}.{filetype}"),
key=music_key(v2), time=music_time(v2), tempo=NULL,
string_names=T,header=header,paper=paper,
midi=F,keep_ly=F,details=F)
The resulting file sarabande2a.png shows the sharps:

Session Info
This document was produced on 13Jan2020 with the following R environment. Note that the tabr package was used in the forked version branch HOQC1:
#> 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] tabraux_0.0.2 tabr_0.4.1
#>
#> loaded via a namespace (and not attached):
#> [1] Rcpp_1.0.3 crayon_1.3.4 dplyr_0.8.3 assertthat_0.2.1
#> [5] R6_2.4.1 magrittr_1.5 evaluate_0.14 pillar_1.4.3
#> [9] rlang_0.4.2 stringi_1.4.3 fs_1.3.1 tools_3.6.0
#> [13] stringr_1.4.0 glue_1.3.1 purrr_0.3.3 xfun_0.10
#> [17] compiler_3.6.0 pkgconfig_2.0.3 tidyselect_0.2.5 knitr_1.26
#> [21] tibble_2.1.3