I am learning how to play piano. In that process, I needed charts with piano chords. I know it is good practise to construct the chords manually, figure out the inversions and so on, but I found that some aspects of learning were improved with a chord chart. I searched the (entire!) internet, and struggled to find good, customisable charts.
Thus I had to make something myself: pichor
, an R
package for making and working with piano chords in R
.
Please find the package’s website at https://mikldk.github.io/pichor (with various tutorials/vignettes at https://mikldk.github.io/pichor/articles).
The source code can be found at https://github.com/mikldk/pichor.
Install the package with the R
command devtools::install_github('mikldk/pichor')
(when devtools
package is installed).
Below, I will briefly demonstrate a few examples.
Examples
Piano chart
library(pichor)
There is built-in data in keys_chords
with information about some of the keys on a piano.
These can be used for e.g. drawing a diagram of a piano:
ggpiano(keys_chords)
It is also possible to highlight certain keys (and show labels):
keys_chords_highlighted <- highlight_keys(keys_chords, keys = c(7, 10, 14))
ggpiano(keys_chords_highlighted, labels = TRUE)
The above is also possible using magrittr
’s pipe, %>%
:
library(magrittr)
keys_chords %>%
highlight_keys(keys = c(7, 10, 14)) %>%
ggpiano(labels = TRUE)
Working with chords
It is possible to work with chords, e.g. getting keys, the highest tone etc.:
chrd <- construct_chord_raw(root_tone = "F#", distances_rel = c(4, 3))
chrd
## F# chord with tones F#/Gb, A#/Bb, C#/Db
as.character(chrd, brief = TRUE)
## [1] "F#"
chrd_Fsm <- construct_chord_minor(root_tone = "F#")
chrd_Fsm
## F#m chord (minor) with tones F#/Gb, A, C#/Db
as.character(chrd_Fsm)
## [1] "F#m chord (minor) with tones F#/Gb, A, C#/Db"
as.character(chrd_Fsm, brief = TRUE)
## [1] "F#m"
get_keys(chord = chrd_Fsm)
## [1] 7 10 14
get_keys_highest_tone(chord = chrd_Fsm, highest_tone = "A")
## [1] 2 7 10
And instead of highlight keys, chords can be highlighted, too:
keys_coords %>%
highlight_chord(chord = chrd_Fsm) %>% # root form by default
ggpiano()
keys_coords %>%
highlight_chord(chord = chrd_Fsm, highest_tone = "A") %>%
ggpiano()
It is also possible to work with chord inversions:
chrd_Bm <- construct_chord_minor(root_tone = "B")
get_keys(chord = chrd_Bm)
## [1] 12 15 19
get_keys_inversion(chord = chrd_Bm, inversion = 0)
## [1] 12 15 19
get_keys_inversion(chord = chrd_Bm, inversion = 1)
## [1] 3 7 12
get_keys_inversion(chord = chrd_Bm, inversion = 2)
## [1] 7 12 15
These can also be highlighted in a chart:
keys_coords %>%
highlight_chord(chord = chrd_Bm, inversion = 2L) %>%
ggpiano()
Chord sequence
It is also possible to work with a sequence of chords. Let me illustrate it with a version of “Let it be” by Beatles that initially has chords G, D, Em, C, G, D, C.
chords <- list(construct_chord_major("G"),
construct_chord_major("D"),
construct_chord_minor("E"), # Em
construct_chord_major("C"),
construct_chord_major("G"),
construct_chord_major("D"),
construct_chord_major("C"))
chords
## [[1]]
## G chord (major) with tones G, B, D
##
## [[2]]
## D chord (major) with tones D, F#/Gb, A
##
## [[3]]
## Em chord (minor) with tones E, G, B
##
## [[4]]
## C chord (major) with tones C, E, G
##
## [[5]]
## G chord (major) with tones G, B, D
##
## [[6]]
## D chord (major) with tones D, F#/Gb, A
##
## [[7]]
## C chord (major) with tones C, E, G
chord_names <- sapply(chords, as.character, brief = TRUE)
key_seq <- lapply(chords, get_keys)
For more advanced plotting and data wrangling, ggplot2
and dplyr
are first loaded.
Helpful facet panel titles are providing by sequence_names
argument to highlight_key_sequence()
:
library(ggplot2)
library(dplyr)
keys_chords %>%
highlight_key_sequence(key_sequence = key_seq,
sequence_names = chord_names) %>%
ggpiano() +
facet_wrap(~ seq_name)
## NULL
And for example include seq_no
in a new column seq_lbl
(that are then converted to a factor with the right ordering):
keys_chords %>%
highlight_key_sequence(key_sequence = key_seq,
sequence_names = chord_names) %>%
mutate(seq_lbl = paste0(seq_name, " (#", seq_no, ")")) %>%
mutate(seq_lbl = forcats::fct_inorder(seq_lbl)) %>%
ggpiano() +
facet_wrap(~ seq_lbl)
## NULL
Finding better inversions
Currently only one method is available for finding better inversions.
It is minimising the distances between keys in consecutive chords, where
distance is the number of keys not used anymore plus new keys.
And with an exhaustive approach that is only feasible for shorter sequences.
opt_res <- optim_min_dist_exhaustive(key_sequence = key_seq)
keys_chords %>%
highlight_key_sequence(key_sequence = opt_res$best,
sequence_names = chord_names) %>%
ggpiano() +
facet_wrap(~ seq_name) +
labs(title = "Best inversions")
## NULL
keys_chords %>%
highlight_key_sequence(key_sequence = opt_res$worst,
sequence_names = chord_names) %>%
ggpiano() +
facet_wrap(~ seq_name) +
labs(title = "Worst inversions")
## NULL