Rendering sheet music is based on building up pieces of musical information culminating in a score. The fundamental object to consider in the transcription context is a phrase. A phrase is created from a noteworthy string and incorporates additional information, most importantly time and rhythm. It can also include positional information such as the instrument string on which a note is played. Outside of rendering tabs, there is no reason to construct phrase objects. Everything from the phrase object on up is about using the R to LilyPond pipeline to render some kind of sheet music document.
A good place to start is with a simple, reproducible example that outlines the basic flow from small, succinct musical phrases, which combine to form tracks, in turn combined into a score, and ending with engraving the score to familiar sheet music. The brief example below recreates the single measure of guitar tablature shown in the
tabr logo. Here are the steps as a list.
phraseor the shorthand alias
The term phrase here simply means any arbitrary piece of musical structure you string together.
phrase takes three main arguments when building a phrase from its component parts. The first gives pitches (or rests) separated in time by spaces. For chords, remove spaces to indicate simultaneous notes. For example, a rest followed by a sequence of pitches might be
notes = "r a, c f d a f".
info is note metadata such as duration. Whole notes are given by 1, half notes by 2, quarter notes 4, and so on, e.g.,
info = "4 8 8 8 8 8 8" (or shorten to
info = "4 8*6"). This example does not require additional information such as dotted notes, staccato notes, ties/slurs, slides, bends, hammer ons and pull offs, etc.
The third argument,
string only applies to fretted string instruments and is always optional. In this example it specifies the strings of a guitar. Providing this information in conjunction with the pitch fixes the frets so that LilyPond does not have to guess them. This only applies for tablature output. Note that the
x shown below is just a placeholder indicating no need to specify a string for the quarter note rest. You can put a string number there but it is ignored.
Explicit string numbers are not needed for this example since lowest fret numbers (Lilypond default) are intended. They are provided for a more complete example.
This is the general approach, but there are multiple ways to create equivalent phrase objects in
phrase(notes = "r a, c f d a f", info = "4 8*6", string = "x 5 5 4 4 3 4")
#> <Musical phrase> #> r4 <a,\5>8 <c\5>8 <f\4>8 <d\4>8 <a\3>8 <f\4>8
Building a phrase from component parts may be necessary in some programmatic contexts. However, when doing manual data entry for simple, interactive examples, the music class offers a higher level of abstraction, sparing you some typing as well as cognitive load.
As an aside, if you are working with the
music class, you can enter notes, note info, and optionally string numbers if applicable, all in one string. This is more efficient for data entry. It can also be easier to follow because it binds the otherwise separate arguments by timestep. See the vignettes and help documentation on music objects for more details.
If you define the music object
as_music("r4 a,8 c f d a f")
#> <Music string> #> Format: space-delimited time #> Values: r4 a,8 c8 f8 d8 a8 f8
it can be passed directly to
phrase, which understands this syntax and interprets the
notes argument as music syntax if the
info argument is not provided (
info = NULL). In fact, the music object does not even need to be previously defined. The string format can be directly provided to
(p1 <- p("r4 a,8 c f d a f"))
#> <Musical phrase> #> r4 <a,>8 <c>8 <f>8 <d>8 <a>8 <f>8
Notice how each timestep is complete within the single character string above. Also, durations (and string numbers) can repeat implicitly until an explicit change occurs.
Track construction is as simple as wrapping a phrase object in
track. This example uses a single phrase. Typically a track would consist of many phrases concatenated together. Tracks are just tibble data frames with an additional track class.
track1 <- track(p1) track1
#> # A tibble: 1 x 7 #> phrase clef key tab tuning voice lyrics #> <list> <chr> <chr> <lgl> <chr> <int> <chr> #> 1 <phrase > treble_8 NA TRUE e,a,dgbe' 1 NA
The complete score is composed of one or more tracks. This example has only a single track. Just as the track constructor takes phrases, the score constructor takes tracks as inputs. Score objects are tibble data frames with an additional score class.
song <- score(track1) song
#> # A tibble: 1 x 8 #> phrase clef key tab tuning voice lyrics id #> <list> <chr> <chr> <lgl> <chr> <int> <chr> <int> #> 1 <phrase > treble_8 NA TRUE e,a,dgbe' 1 NA 1
Once a score object is created, it is ready to be sent to LilyPond. If LilyPond is installed on your system (and added to your system PATH variable on Windows systems),
tab should call it successfully. Alternatively, on Windows, it can be added explicitly by calling
tabr_options. This option to specify the LilyPond path is still available on other systems. An example of this is commented out below. However,
tabr will do its best on package load to set these paths in
tabr_options for you if it can successfully detect a LilyPond installation in a standard file system location, so you may not have to do anything. Just check
tabr_options() after you load the package. If any of the paths are equal to the empty string
"", then you need to set the paths. Otherwise you should be ready to run LilyPond from R.
# may not be necessary # tabr_options(lilypond = "C:/Program Files (x86)/LilyPond/usr/bin/lilypond.exe")
Technically, when loading
tabr on Windows it will attempt to check the specific path above to see if
lilypond.exe exists there. If it does, the path in
tabr_options will be filled in for you. Therefore, if you need to specify the LilyPond path because it is not in your PATH environment variable and nothing shows in
tabr_options(), then the path shown above is probably not where you installed LilyPond.
Other than ensuring LilyPond is accessible, there is only one function left to call in this series of steps to produce the sheet music.
tab takes several arguments, many of which are optional and have default values. However, since music is so variable it makes sense to specify these arguments every time. These arguments include metadata about the piece such as key signature, time signature, tempo, title, author, and so on. Most of that is not needed here, but for good practice at least be unambiguous about the music itself and specify a key signature, time signature and tempo.
tab(song, "phrase.pdf", key = "dm", time = "4/4", tempo = "4 = 120")
#> #### Engraving score to phrase.pdf #### #> GNU LilyPond 2.18.2 #> Processing `./phrase.ly' #> Parsing... #> Interpreting music... #> Preprocessing graphical objects... #> Interpreting music... #> MIDI output to `./phrase.mid'... #> Finding the ideal number of pages... #> Fitting music on 1 page... #> Drawing systems... #> Layout output to `./phrase.ps'... #> Converting to `./phrase.pdf'... #> Success: compilation successfully completed
This log output will be printed at the R console. Load the new pdf file to see the result. It should look like this.
Note above that
tabr also exports the pipe
%>% operator. Even given the hierarchy of objects involved in the series of steps to move from a phrase to a rendered pdf, a short example like this does not even require a single assignment. The same steps involved in building up the structure and rendering the output could be performed as follows.
While music can be quite complex and a full score will be much longer,
tabr strives to minimize the work while still forcing some sense of interpretable, organized structure. For long and complex music, it can require some effort and practice to ensure your approach to transcription in your R code is not opaque.
To recap, the
phrase is defined, added to a
track, the track is added to a
score, and the score is rendered to tablature in a pdf file with
tab. The next tutorial section discusses phrases in more detail and provides an overview of the different notes and note metadata that can be specified when constructing a phrase.