Tabr
R package: tabr. Notation-based and tidy music data analysis and transcription.
Install / Use
/learn @leonawicz/TabrREADME
tabr <img src="man/figures/logo.png" align="right" alt="" width="120" />
<!-- badges: start --> <!-- badges: end --> <img src="man/figures/README-banner-1.png" width="85%" id="tabr-readme-banner" />Overview
The tabr package 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; i.e, more from the perspective and in the language of a musician than, say, an audio engineer.
Citation
Matthew Leonawicz (2026). tabr: Music Notation Syntax, Manipulation, Analysis and Transcription in R. R package version 0.5.5. https://CRAN.R-project.org/package=tabr
Contribute
Contributions are welcome. Contribute through GitHub via pull request. Please create an issue first if it is regarding any substantive feature add or change.
Installation
Install the CRAN release of tabr with
install.packages("tabr")
Install the development version from GitHub with
# install.packages("remotes")
remotes::install_github("leonawicz/tabr")
Motivating example
as_music("r8 c d e f g a b c'1") |> plot_music_guitar()
<img src="man/figures/README-plot_music-1.png" width="50%" style="display: block; margin: auto;" />
Music data structures
It’s easiest to begin with a high level view using some basic examples.
Music data can be viewed, manipulated and analyzed while in different forms of representation based around different data structures: strings and data frames. Each representation offers advantages over the other for different use cases.
Music syntax can be entered directly and represented in character
strings to minimize the formatting overhead of data entry by using
simple data structures, for example when wanting to quickly enter and
transcribe short pieces of music syntax in R into sheet music or
tablature files. You can also enter sound and time together with the
music class, and no need to repeat consecutive durations until a
change.
Here is an example of making a string of pitches noteworthy and a string of combined pitch and duration into a music object. Such objects carry various musical information based on the syntax of an input string.
library(tabr)
x <- "a, c e g# a ac'e' ac'e'~ ac'e' a c' e' a'"
x <- as_noteworthy(x)
x
#> <Noteworthy string>
#> Format: space-delimited time
#> Values: a, c e g# a <ac'e'> <ac'e'~> <ac'e'> a c' e' a'
summary(x)
#> <Noteworthy string>
#> Timesteps: 12 (9 notes, 3 chords)
#> Octaves: tick
#> Accidentals: sharp
#> Format: space-delimited time
#> Values: a, c e g# a <ac'e'> <ac'e'~> <ac'e'> a c' e' a'
y <- "a,8 c et8 g# a ac'e'4. ac'e'~8 ac'e'4 at4 c' e' a'1"
y <- as_music(y)
summary(y)
#> <Music string>
#> Timesteps: 12 (9 notes, 3 chords)
#> Octaves: tick
#> Accidentals: sharp
#> Key signature: c
#> Time signature: 4/4
#> Tempo: 2 = 60
#> Lyrics: NA
#> Format: space-delimited time
#> Values: a,8 c8 et8 g#t8 at8 <ac'e'>4. <ac'e'~>8 <ac'e'>4 at4 c't4 e't4 a'1
music_split(y)
#> $notes
#> <Noteworthy string>
#> Format: space-delimited time
#> Values: a, c e g# a <ac'e'> <ac'e'~> <ac'e'> a c' e' a'
#>
#> $info
#> <Note info string>
#> Format: space-delimited time
#> Values: 8 8 t8 t8 t8 4. 8 4 t4 t4 t4 1
#>
#> $lyrics
#> [1] NA
#>
#> $key
#> [1] "c"
#>
#> $time
#> [1] "4/4"
#>
#> $tempo
#> [1] "2 = 60"
Functions exist for directly performing various mathematical, logical
and organizational operations and musical transformations on strings
like the one above by checking their music syntax validity and adding
custom classes and methods to these strings (more on this below). tabr
offers special object classes that facilitate working with music data
and notation in ways that are natural to R, robust, tidy, and lend
themselves well to transcription as well as analysis.
Of course, none of this will work on character strings that are not “noteworthy” or “musical”, for example. Invalid, unworthy syntax is rejected early with an error, preventing corrupted music syntax from causing unexpected issues later on.
The same music data can also be organized in tidy data frames, allowing for a more familiar and powerful approach to the analysis of large amounts of structured music data.
x <- "a,8 c e r r c a, g#, a ac'e'"
as_music(x) |> as_music_df()
#> # A tibble: 10 × 14
#> duration pitch note semitone octave freq pitch_int scale_int slur slide
#> <chr> <chr> <chr> <int> <int> <dbl> <int> <chr> <chr> <lgl>
#> 1 8 a, a 57 2 110. NA <NA> <NA> FALSE
#> 2 8 c c 48 3 131. 3 m3 <NA> FALSE
#> 3 8 e e 52 3 165. 4 M3 <NA> FALSE
#> 4 8 r r NA NA NA NA <NA> <NA> FALSE
#> 5 8 r r NA NA NA NA <NA> <NA> FALSE
#> 6 8 c c 48 3 131. -4 M3 <NA> FALSE
#> 7 8 a, a 57 2 110. -3 m3 <NA> FALSE
#> 8 8 g#, g# 56 2 104. -1 m2 <NA> FALSE
#> 9 8 a a 57 3 220 13 m9 <NA> FALSE
#> 10 8 ac'e' ace 57 3 220 0 P1 <NA> FALSE
#> # ℹ 4 more variables: bend <lgl>, dotted <int>, articulation <chr>,
#> # annotation <chr>
Several functions are available for mapping seamlessly between and manipulating these data structures and their representations of musical information.
Noteworthy strings
It is helpful to have a deeper understanding of how this music notation syntax informs data structures and operations integrated throughout the package.
As a quick introduction and to get oriented to the music notation syntax
offered by tabr, consider the concept of a noteworthy string. This is
like any other character string, except that what makes a string
noteworthy is that its content consists strictly of valid tabr music
notation syntax. It can be parsed unambiguously and meaningfully as
input by package functions that inspect and manipulate musical
information.
A bit about basic syntax
A simple character string like "c e g", or alternatively as a vector,
c("c", "e", "g"), is a noteworthy string. The single lowercase letter
"a" is noteworthy. So are "a_" and "a#" (flat and sharp). However,
"A" is not (case sensitivity), nor is "z". Of course, as seen above,
there is more valid syntax than just the lowercase musical note letters
a through g and sharp and flat notation.
An important piece of syntax is the octave. In conjunction with a note,
specifying a unique pitch requires the octave number, either in tick
format (comma and single quote, c, c c') or integer format
(c2 c c4). Octave 3 is the implicit default; there is no tick in tick
format and explicitly adding the 3 in integer format is unnecessary.
The pitches c d e f g a b (default octave) are the notes in the octave
below middle C (c').
You’ve already seen above with the example using a music object that a
noteworthy string can be one part of a more complete piece of musical
information. Tick format is necessary to avoid ambiguity with respect to
temporal information once two such pieces of information are merged
together. For this reason, tick format is preferred in general. Tick
format also matches that used by the LilyPond music engraving software,
which is used by the tabr LilyPond API for anything transcription
related like rendering sheet music to PDF or in R markdown documents.
For all available syntax specifications and related details see the package vignettes.
The noteworthy class
Noteworthiness can be checked on any character string. When defining
noteworthy strings you can define them like any other character vector.
However, you will notice that package functions that operate on
noteworthy strings and whose output is another noteworthy string will
yield a string with the supplemental noteworthy class. This has its
own print() and summary() methods.
Several other generic methods are also implemented for the noteworthy
class, making it easy to perform simple but powerful operations on these
objects in a familiar way. While many functions will attempt to coerce a
string to noteworthy, not all will and some methods are implemented
specifically for the class.
x <- "g#, c d# g#c'd#'"
as_noteworthy(x)
#> <Noteworthy string>
#> Format: space-delimited time
#> Values: g#, c d# <g#c'd#'>
is_note(x)
#> [1] TRUE TRUE TRUE FALSE
is_chord(x)
#> [1] FALSE FALSE FALSE TRUE
chord_is_major(x)
#> [1] NA NA NA TRUE
(x <- transpose(x, 1))
#> <Noteworthy string>
#> Format: space-delimited time
#> Values: a, c# e <ac#'e'>
summary(x)
#> <Noteworthy string>
#> Timesteps: 4 (3 notes, 1 chord)
#> Octaves: tick
#> Accidentals: sharp
#> Format: space-delimited time
