Can we build and play music by manipulating tidy data frames ?
The {gm} package by Renfei Mao is young and promising, this package is a wrapper of {gm} where I play with some ideas. You’ll need to install {gm} first.
Install with
remotes::install_github("moodymudskipper/tidygm")
I’ll illustrate {tidygm} by recreating a version of Coldplay’s song “Clocks”. I followed this great breakdown of the song by Rick Beato :
https://www.youtube.com/watch?v=7_warUFthJI&ab_channel=RickBeato
library(tidygm)
library(magrittr, include.only = "%>%")
#> Warning: package 'magrittr' was built under R version 4.0.4
The first bar of “Clocks” is a piano line that goes :
Eb6, Bb5, G5, Eb6, Bb5, G5, Eb6, Bb5
Let’s make a song out of it using the function new_song
, which creates
a tidy data frame, and play
which plays it.
- The song is in F minor, the tempo is 129 bpm
- The meter is 4/4 (which is the default for
new_song
) - We don’t provide a
durations
argument sonew_song
will guess these are all eighth notes - The default instrument is “piano” (at the moment it is the only instrument supported by {gm} but this should hopefully change with the next minor release).
piano_1 <- list("E-6", "B-5", "G5", "E-6", "B-5", "G5", "E-6", "B-5")
song <- new_song(list(piano_1), key = "Fm", tempo = 129)
song
#> # A tibble: 1 x 7
#> bar_id tempo key meter instrument pitches durations
#> <int> <dbl> <chr> <chr> <chr> <list> <list>
#> 1 1 129 Fm 4/4 piano <list [8]> <list [8]>
play(song)
This worked well! let’s write two bars in one go now :
piano_2 <- list("C#6", "A#5", "F5", "C#6", "A#5", "F5", "C#6", "A#5")
song <- new_song(list(piano_1, piano_2), key = "Fm", tempo = 129)
song
#> # A tibble: 2 x 7
#> bar_id tempo key meter instrument pitches durations
#> <int> <dbl> <chr> <chr> <chr> <list> <list>
#> 1 1 129 Fm 4/4 piano <list [8]> <list [8]>
#> 2 2 129 Fm 4/4 piano <list [8]> <list [8]>
play(song)
We don’t want to build the object from scratch every time, so we provide utilities to build our song data frame as we go.
The 3rd is the same as the 2nd, so we copy and paste it :
song <- song %>% copy_and_paste(bar_id == 2)
song
#> # A tibble: 3 x 7
#> bar_id tempo key meter instrument pitches durations
#> <dbl> <dbl> <chr> <chr> <chr> <list> <list>
#> 1 1 129 Fm 4/4 piano <list [8]> <list [8]>
#> 2 2 129 Fm 4/4 piano <list [8]> <list [8]>
#> 3 3 129 Fm 4/4 piano <list [8]> <list [8]>
play(song)
copy_and_paste
has additional arguments subset_to
, to indicate where
to paste (to overwrite values), and what
to indicate what to paste,
but if left empty as done here we just copy everything to the end, with
new values of bar_id
.
The 4th bar is different, since we cannot copy and paste it, we add a
new bar using add_bars()
piano_4 <- list("C6", "A-5", "F5", "C6", "A-5", "F5", "C6", "A-5")
song <- add_bars(song, list(piano_4))
song
#> # A tibble: 4 x 7
#> bar_id tempo key meter instrument pitches durations
#> <dbl> <dbl> <chr> <chr> <chr> <list> <list>
#> 1 1 129 Fm 4/4 piano <list [8]> <list [8]>
#> 2 2 129 Fm 4/4 piano <list [8]> <list [8]>
#> 3 3 129 Fm 4/4 piano <list [8]> <list [8]>
#> 4 4 129 Fm 4/4 piano <list [8]> <list [8]>
play(song)