You have already seen is_chord()
, which is similar to
is_note()
. Another check you have seen is
is_diatonic()
. You can also check whether a chords are
major or minor, but this is imperfect due to the inability to know if
the user is interpreting their notation of a chord as an inversion.
Instances where it is too difficult to tell, or inapplicable such as
with single notes, return NA
.
x <- "b c ce_g cd#g"
is_diatonic(x, key = "b_")
#> [1] FALSE TRUE TRUE FALSE
#> [1] NA NA FALSE FALSE
#> [1] NA NA TRUE TRUE
A few functions that compare chords are chord_rank()
,
chord_order()
and chord_sort()
. Ranking
chords, and the ordering and sorting based on that, requires a
definition or set of definitions to work from.
The first argument is a noteworthy string. The second,
pitch
, can be "min"
(the default),
"mean"
, or "max"
. Each of these refers to the
functions that operate on the three available definitions of ranking
chords. When ranking individual notes, the result is fixed because there
are only two pitches being compared. For chords, however,
pitch = "min"
compares only the lowest pitch or root note
of a chord. For pitch = "max"
, the highest pitch note in
each chord is used for establishing rank. For
pitch = "mean"
, the average of all notes in the chord are
used for ranking chords.
Rank is from lowest to highest pitch. These options define how chords
are ranked, but each function below also passes on additional arguments
via ...
to the base functions rank()
and
order()
for the additional control over the more general
aspects of how ranking and ordering are done in R.
chord_order()
works analogously to
chord_rank()
. chord_sort()
wraps around
chord_order()
.
x <- "a2 c a2 ceg ce_g cea"
chord_rank(x, "min")
#> [1] 1.5 4.5 1.5 4.5 4.5 4.5
chord_rank(x, "max")
#> [1] 1.5 3.0 1.5 4.5 4.5 6.0
chord_rank(x, "mean")
#> [1] 1.5 3.0 1.5 5.0 4.0 6.0
chord_order(x)
#> [1] 1 3 2 4 5 6
chord_order(x, "mean")
#> [1] 1 3 2 5 4 6
chord_sort(x, "mean")
#> <Noteworthy string>
#> Format: space-delimited time
#> Values: a2 a2 c <ce_g> <ceg> <cea>
Chords can be sliced or indexed using the functions
chord_root()
, chord_top()
and
chord_slice()
. The first two are special cases of
chord_slice
. The first two functions return a noteworthy
string containing only the root or top notes of each chord. If the
string contains a single note, by definition the note is returned.
For chord_slice()
, however, an integer index range is
provided and it is possible to reduce a note or chord to nothing by
passing indices that are completely out of bounds. Any note or chord
that is completely sliced away is dropped.
The example below also shows that what matters is pitch order, not the order in which notes in a chord are entered in the string.
x <- "a2 ceg e_gc egc,cc'"
chord_root(x)
#> <Noteworthy string>
#> Format: space-delimited time
#> Values: a, c c c,
chord_top(x)
#> <Noteworthy string>
#> Format: space-delimited time
#> Values: a, g g c'
identical(chord_slice(x, 1), chord_root(x))
#> [1] TRUE
chord_slice(x, 2)
#> <Noteworthy string>
#> Format: space-delimited time
#> Values: e e_ c
chord_slice(x, 4)
#> <Noteworthy string>
#> Format: space-delimited time
#> Values: g
chord_slice(x, 3:5)
#> <Noteworthy string>
#> Format: space-delimited time
#> Values: g g <egc'>
The slicing functions deal with position within a chord;
they are not a simple reproduction of vector indexing of time steps,
which is trivial and can already be done with note_slice()
(clearly not slicing a single note, but a noteworthy string). Filtering
the sequence rather than the elements within it is best done by
taking the results of a function that returns a logical vector and
passing them to note_slice()
. This tends to fall under the
topic of general noteworthy string functions and does not apply strictly
to chords, but an example is shown here.
x <- "a2 ceg e_gc egc,cc'"
note_slice(x, 3:4)
#> <Noteworthy string>
#> Format: space-delimited time
#> Values: <e_gc> <egc,cc'>
note_slice(x, is_chord(x))
#> <Noteworthy string>
#> Format: space-delimited time
#> Values: <ceg> <e_gc> <egc,cc'>
A broken chord can be created with chord_break()
, which
separates a chord into its component notes, separating in time. It
accepts a single chord.
x <- "ce_g"
chord_break(x)
#> <Noteworthy string>
#> Format: space-delimited time
#> Values: c e_ g
chord_invert()
creates chord inversions. It also takes a
single chord as input. It treats any chord as being in root position as
provided. The example below applies the function over a series of
inversion values to show how the output changes.
pc(sapply((-3):3, function(i) chord_invert(x, i)))
#> <Noteworthy string>
#> Format: space-delimited time
#> Values: <c2e_2g2> <e_2g2c> <g2ce_> <ce_g> <e_gc4> <gc4e_4> <c4e_4g4>
While a chord with n
notes has n - 1
inversions, chord_invert()
allows inversions to continue,
moving a chord further up or down in octaves. If you want to restrict
the function to only allowing the defined number of inversions
(excluding root position), set limit = TRUE
. This enforces
the rule that, for example, a chord with three notes has two inversions
and n
can only take values between -2
and
2
or it will throw and error.
Building up on chord_invert()
,
chord_arpeggiate()
grows a chord up or down the scale in
pitch by creating an arpeggio. n
describes how many steps
to add onto the original chord. Setting by = "chord"
will
replicate the entire chord as is, up or down the scale. In this case
n
indicates whole octave transposition steps. By default,
n
refers to the number of steps that individual chord notes
are arpeggiated, like in chord_invert()
. This means for
example that in a chord with three notes, setting n = 3
and
by = "note"
is equivalent to setting n = 1
and
by = "chord"
.
The argument broken = TRUE
will also convert to a broken
chord, resulting in an arpeggio of individual notes.
chord_arpeggiate("ce_gb_", 2)
#> <Noteworthy string>
#> Format: vectorized time
#> Values: <ce_gb_> <e_gb_c4> <gb_c4e_4>
chord_arpeggiate("ce_gb_", -2)
#> <Noteworthy string>
#> Format: vectorized time
#> Values: <ce_gb_> <b_2ce_g> <g2b_2ce_>
chord_arpeggiate("ce_gb_", 2, by = "chord")
#> <Noteworthy string>
#> Format: vectorized time
#> Values: <ce_gb_> <c4e_4g4b_4> <c5e_5g5b_5>
chord_arpeggiate("ce_gb_", 1, broken = TRUE, collapse = TRUE)
#> <Noteworthy string>
#> Format: space-delimited time
#> Values: c e_ g b_ e_ g b_ c4
Before introducing the chord constructors, here is a brief mention
and example of the dyad()
function for constructing dyads
from a root note and and interval. Dyads are not always considered
chords, but this is as good a place as any to mention
dyad()
since the key distinction made in tabr
in this context is whether there is a single note or multiple notes. The
interval passed to dyad()
can be in semitones, or a named
interval from mainIntervals
that corresponds to the number
of semitones.
dyad("a", 3)
#> <Noteworthy string>
#> Format: space-delimited time
#> Values: <ac'>
#> <Noteworthy string>
#> Format: space-delimited time
#> Values: <ac'> <ac'> <ac'> <ac'>
dyad("c'", x, reverse = TRUE)
#> <Noteworthy string>
#> Format: space-delimited time
#> Values: <ac'> <ac'> <ac'> <ac'>
Now to the topic of chord construction, there are two general forms
of chord construction currently available in tabr
. The
first is for typical chords based on their defining intervals; i.e.,
“piano chords”. These are not particularly useful for guitar-specific
chord shapes and fingerings, which generally span a greater pitch range.
See further below for guitar chords.
In tabr
chords are often constructed from scratch by
explicitly typing the chord pitches in a noteworthy string, but many
chords can also be constructed using helper functions. Currently,
helpers exist for common chords up through thirteenths.
tabr
offers two options for each chord constructor function
name: the longer chord_*
-named function and its
x*
alias. The table below shows all available
constructors.
#> full_name abbreviation
#> 1 chord_min xm
#> 2 chord_maj xM
#> 3 chord_min7 xm7
#> 4 chord_dom7 x7
#> 5 chord_7s5 x7s5
#> 6 chord_maj7 xM7
#> 7 chord_min6 xm6
#> 8 chord_maj6 xM6
#> 9 chord_dim xdim
#> 10 chord_dim7 xdim7
#> 11 chord_m7b5 xm7b5
#> 12 chord_aug xaug
#> 13 chord_5 x5
#> 14 chord_sus2 xs2
#> 15 chord_sus4 xs4
#> 16 chord_dom9 x9
#> 17 chord_7s9 x7s9
#> 18 chord_maj9 xM9
#> 19 chord_add9 xadd9
#> 20 chord_min9 xm9
#> 21 chord_madd9 xma9
#> 22 chord_min11 xm11
#> 23 chord_7s11 x7s11
#> 24 chord_maj7s11 xM7s11
#> 25 chord_11 x_11
#> 26 chord_maj11 xM11
#> 27 chord_13 x_13
#> 28 chord_min13 xm13
#> 29 chord_maj13 xM13
These functions take root notes and a key signature as input. The given function determines the intervals of the chord. This in combination with a root note is all that is needed to create the chord. However, the key signature can enforce whether the result uses flats or sharps when accidentals are present.
chord_min7("a c e")
#> <Noteworthy string>
#> Format: space-delimited time
#> Values: <ac'e'g'> <ce_gb_> <egbd'>
chord_min7("a c e", key = "f")
#> <Noteworthy string>
#> Format: space-delimited time
#> Values: <ac'e'g'> <ce_gb_> <egbd'>
xm7("a c e", key = "f")
#> <Noteworthy string>
#> Format: space-delimited time
#> Values: <ac'e'g'> <ce_gb_> <egbd'>
The dataset guitarChords
is a tibble containing 3,967
rows of predefined guitar chords. It is highly redundant, but convenient
to use. It is generated from a much smaller chords basis set, that is
then transposed over all notes, yielding chord types and shapes for all
twelve notes. Chords begin from open position and range up one octave to
chords whose lowest fret is eleven. There are also multiple chord
voicings for many chord types. Finally, chords containing accidentals
are included in the table with both the flat and sharp versions.
There are twelve columns. Again, some of the column-wise information is also redundant, but it is not a big deal to include and removes the need to do a variety of computations to map from one representation of chord information to another. Here are the first ten rows:
guitarChords
#> # A tibble: 3,967 × 12
#> id lp_name root octave root_fret min_fret bass_string notes frets
#> <fct> <chr> <chr> <dbl> <dbl> <dbl> <int> <chr> <chr>
#> 1 M a,:5 a 2 0 0 5 a,eac#'e' xo222o
#> 2 M a,:5 a 2 0 0 5 a,ead_'e' xo222o
#> 3 m a,:m a 2 0 0 5 a,eac'e' xo221o
#> 4 7 a,:7 a 2 0 0 5 a,egc#'e' xo2o2o
#> 5 7 a,:7 a 2 0 0 5 a,egd_'e' xo2o2o
#> 6 M7 a,:maj7 a 2 0 0 5 a,eg#c#'e' xo212o
#> 7 M7 a,:maj7 a 2 0 0 5 a,ea_d_'e' xo212o
#> 8 m7 a,:m7 a 2 0 0 5 a,egc'e' xo2o1o
#> 9 sus2 a,:sus2 a 2 0 0 5 a,eabe' xo22oo
#> 10 sus4 a,:sus4 a 2 0 0 5 a,ead'e' xo223o
#> # ℹ 3,957 more rows
#> # ℹ 3 more variables: semitones <list>, fretboard <chr>, open <lgl>
You can also define your own chords using chord_def()
.
All you need are the fret numbers for the fretted chord. Currently it is
assumed to be a six-string instrument. The default tuning is standard,
but this can be changed arbitrarily. NA
indicates a muted
string. Order is from lowest pitch string to highest. In the example
below, create a set of minor chords based on the open Am
shape.
frets <- c(NA, 0, 2, 2, 1, 0)
chord_def(frets, "m", 6) # sixth entry (highest string: string #1) is optional
#> # A tibble: 1 × 13
#> id lp_name root octave root_fret min_fret bass_string notes frets
#> <chr> <chr> <chr> <int> <dbl> <dbl> <int> <chr> <chr>
#> 1 m a,:m a 2 0 0 5 a,eac'e' xo221o
#> # ℹ 4 more variables: semitones <list>, optional <chr>, fretboard <chr>,
#> # open <lgl>
guitarChords
does not currently contain the
optional
column, but this is a column where you can
indicate optional chord notes, as shown above.
chord_def()
is scalar and defines a single chord, always
returning a table with one row, but you can map over it however you need
in order to define a collection of chords. Below, a set of chords is
generated with sharps and again with flats.
#> # A tibble: 12 × 13
#> id lp_name root octave root_fret min_fret bass_string notes frets
#> <chr> <chr> <chr> <int> <dbl> <dbl> <int> <chr> <chr>
#> 1 m b_,:m b_ 2 1 1 5 b_,fb_d_'f' x133…
#> 2 m b,:m b 2 2 2 5 b,g_bd'g_' x244…
#> 3 m c:m c 3 3 3 5 cgc'e_'g' x355…
#> 4 m d_:m d_ 3 4 4 5 d_a_d_'e'a_' x466…
#> 5 m d:m d 3 5 5 5 dad'f'a' x577…
#> 6 m e_:m e_ 3 6 6 5 e_b_e_'g_'b_' x688…
#> 7 m e:m e 3 7 7 5 ebe'g'b' x799…
#> 8 m f:m f 3 8 8 5 fc'f'a_'c'' x8(1…
#> 9 m g_:m g_ 3 9 9 5 g_d_'g_'a'd_… x9(1…
#> 10 m g:m g 3 10 10 5 gd'g'b_'d'' x(10…
#> 11 m a_:m a_ 3 11 11 5 a_e_'a_'b'e_… x(11…
#> 12 m a:m a 3 12 12 5 ae'a'c''e'' x(12…
#> # ℹ 4 more variables: semitones <list>, optional <lgl>, fretboard <chr>,
#> # open <lgl>
#> # A tibble: 12 × 13
#> id lp_name root octave root_fret min_fret bass_string notes frets
#> <chr> <chr> <chr> <int> <dbl> <dbl> <int> <chr> <chr>
#> 1 m b_,:m b_ 2 1 1 5 b_,fb_d_'f' x133…
#> 2 m b,:m b 2 2 2 5 b,g_bd'g_' x244…
#> 3 m c:m c 3 3 3 5 cgc'e_'g' x355…
#> 4 m d_:m d_ 3 4 4 5 d_a_d_'e'a_' x466…
#> 5 m d:m d 3 5 5 5 dad'f'a' x577…
#> 6 m e_:m e_ 3 6 6 5 e_b_e_'g_'b_' x688…
#> 7 m e:m e 3 7 7 5 ebe'g'b' x799…
#> 8 m f:m f 3 8 8 5 fc'f'a_'c'' x8(1…
#> 9 m g_:m g_ 3 9 9 5 g_d_'g_'a'd_… x9(1…
#> 10 m g:m g 3 10 10 5 gd'g'b_'d'' x(10…
#> 11 m a_:m a_ 3 11 11 5 a_e_'a_'b'e_… x(11…
#> 12 m a:m a 3 12 12 5 ae'a'c''e'' x(12…
#> # ℹ 4 more variables: semitones <list>, optional <lgl>, fretboard <chr>,
#> # open <lgl>
Several functions are available that glean specific information from
guitarChords
. The functions lp_chord_id()
and
lp_chord_mod()
obtain the LilyPond chord name and LilyPond
chord modifier (suffix), given a root note and tabr
format
chord name/modifier. It is not quite identical to LilyPond format (set
exact = TRUE
). See function documentation for details.
lp_chord_id("a a a", "m M m7_5")
#> [1] "a:m" "a:5" "a:m7_5"
lp_chord_mod("a a a", "m M m7_5")
#> [1] "m" "5" "m7_5"
gc_is_known()
checks against guitarChords
to see if the explicit chord, as given by its component pitches, exists
in the dataset. Notice below that a
in the string of notes
is a note, not a major A
chord, and hence returns
FALSE
. In the gc_name_*
functions, the same
a
is now interpreted as a major chord because these
functions operate on chord name inputs.
gc_is_known("a b_,fb_d'f'")
#> [1] FALSE TRUE
x <- "a aM b_,m7#5"
gc_name_split(x)
#> # A tibble: 3 × 2
#> root mod
#> <chr> <chr>
#> 1 a M
#> 2 a M
#> 3 b_, m7#5
gc_name_root(x)
#> [1] "a" "a" "b_,"
gc_name_mod(x)
#> [1] "M" "M" "m7#5"
The most interesting use of guitarChords
is in using it
to map from chord names to noteworthy strings.
gc_info()
can be used to filter
guitarChords
. In the examples below, you can see that
multiple chord names can be supplied at once. All are used to filter the
chord dataset. If the inputs do not exist, an empty tibble with zero
rows is returned. The result is not vectorized to match the number of
entries in the input; it is simply a row filter for
guitarChords
.
gc_info("a") # a major chord, not a single note
#> # A tibble: 6 × 12
#> id lp_name root octave root_fret min_fret bass_string notes frets
#> <fct> <chr> <chr> <dbl> <dbl> <dbl> <int> <chr> <chr>
#> 1 M a,:5 a 2 0 0 5 a,ead_'e' xo222o
#> 2 M a,:5 a 2 5 5 6 a,ead_'e'a' 577655
#> 3 M a,:5 a 2 5 2 6 a,d_ead_'a' 542225
#> 4 M a:5 a 3 7 7 4 ae'a'd_'' xx79(1…
#> 5 M a:5 a 3 12 12 5 ae'a'd_''e'' x(12)(…
#> 6 M a:5 a 3 12 9 5 ad_'e'a'd_'' x(12)(…
#> # ℹ 3 more variables: semitones <list>, fretboard <chr>, open <lgl>
gc_info("ceg a#m7_5") # only third entry is a guitar chord
#> # A tibble: 6 × 12
#> id lp_name root octave root_fret min_fret bass_string notes frets
#> <fct> <chr> <chr> <dbl> <dbl> <dbl> <int> <chr> <chr>
#> 1 m7_5 b_,:m7_5 b_ 2 1 0 5 b_,a_d_'e' x1x12o
#> 2 m7_5 b_,:m7_5 b_ 2 1 1 5 b_,ea_d_' x1212x
#> 3 m7_5 b_,:m7_5 b_ 2 6 5 6 b_,a_d_'e' 6x665x
#> 4 m7_5 b_:m7_5 b_ 3 8 4 4 b_d_'e'a_' xx8654
#> 5 m7_5 b_:m7_5 b_ 3 8 8 4 b_e'a_'d_'' xx8999
#> 6 m7_5 b_:m7_5 b_ 3 13 12 5 b_a_'d_''e'' x(13)…
#> # ℹ 3 more variables: semitones <list>, fretboard <chr>, open <lgl>
gc_info("ceg a#m7_5", key = "f")
#> # A tibble: 6 × 12
#> id lp_name root octave root_fret min_fret bass_string notes frets
#> <fct> <chr> <chr> <dbl> <dbl> <dbl> <int> <chr> <chr>
#> 1 m7_5 b_,:m7_5 b_ 2 1 0 5 b_,a_d_'e' x1x12o
#> 2 m7_5 b_,:m7_5 b_ 2 1 1 5 b_,ea_d_' x1212x
#> 3 m7_5 b_,:m7_5 b_ 2 6 5 6 b_,a_d_'e' 6x665x
#> 4 m7_5 b_:m7_5 b_ 3 8 4 4 b_d_'e'a_' xx8654
#> 5 m7_5 b_:m7_5 b_ 3 8 8 4 b_e'a_'d_'' xx8999
#> 6 m7_5 b_:m7_5 b_ 3 13 12 5 b_a_'d_''e'' x(13)…
#> # ℹ 3 more variables: semitones <list>, fretboard <chr>, open <lgl>
gc_info("a,m c d f,")
#> # A tibble: 23 × 12
#> id lp_name root octave root_fret min_fret bass_string notes frets
#> <fct> <chr> <chr> <dbl> <dbl> <dbl> <int> <chr> <chr>
#> 1 m a,:m a 2 0 0 5 a,eac'e' xo221o
#> 2 m a,:m a 2 5 5 6 a,eac'e'a' 577555
#> 3 m a,:m a 2 5 2 6 a,cea 5322xx
#> 4 m a:m a 3 7 7 4 ae'a'c'' xx79(1…
#> 5 m a:m a 3 12 12 5 ae'a'c''e'' x(12)(…
#> 6 m a:m a 3 12 9 5 ac'e'a' x(12)(…
#> 7 M c:5 c 3 3 3 5 cgc'e'g' x35553
#> 8 M c:5 c 3 3 0 5 cegc'e' x32o1o
#> 9 M c:5 c 3 8 8 6 cgc'e'g'c'' 8(10)(…
#> 10 M c:5 c 3 8 5 6 cegc'e'c'' 875558
#> # ℹ 13 more rows
#> # ℹ 3 more variables: semitones <list>, fretboard <chr>, open <lgl>
The same properties of gc_info()
apply to wrapper
functions around it, namely, gc_notes()
and
gc_fretboard()
.
gc_notes()
takes chord names that exist in
guitarChords
and returns the noteworthy strings needed for
phrase construction. Remember, the is just a basic filter. If you
specify chords imprecisely, the result will contain many more chords
than were in the input. If you specify chords completely unambiguously,
then there is one result for each input. However, this also requires you
do not provide any chord names that are not in
guitarChords
, or these will be dropped.
Keep in mind that these functions are under active development and
the approaches they take may change prior to the next CRAN release of
tabr
. Currently, the ways you can add precision to your
chord mapping include passing the following optional arguments:
root_octave
: the octave number for the root note.root_fret
: the fret for the root note.min_fret
: the lowest fret position for a chord.bass_string
: the lowest unmuted string for a
chord.open
: open chords, closed/movable chords, or both
(default).key
: key signature, to enforce sharps or flats when
present.In this example, possible matches from guitarChord
are
filtered to any whose root fret is in 0:2
.
#> <Noteworthy string>
#> Timesteps: 2 (0 notes, 2 chords)
#> Octaves: tick
#> Accidentals: flat
#> Format: space-delimited time
#> Values: <a,egd_'e'> <b,g_bd'g_'>
Notice that the octave information helps further restrict the chord
set with ignore_octave = FALSE
.
When creating tablature and sheet music with LilyPond, you may wish
to include a chord chart containing fretboard diagrams of the chords as
they are played. Currently, tabr
uses the
chord_set()
function to prepare a named character vector of
chords that have quasi-LilyPond chord names and fretboard notation
values, ready to be passed to score()
for proper injection
into LilyPond.
This process and the structure of the data objects involved may
change soon (I have not decided for certain yet). But for the time
being, this is still the process. Therefore, the new function
gc_fretboard()
performs the same manipulation using chords
from guitarChords
.
gc_fretboard("a,m c d f,", min_fret = 0:1)
#> a,:m c:5 d:5 f,:5
#> "x;o;2;2;1;o;" "x;3;2;o;1;o;" "x;x;o;2;3;2;" "1;3;3;2;1;1;"