Complete Guide to gm

library(gm)

There are two interpretations of the package name gm:

  1. grammar of music
  2. generate music

They correspond to two functionalities of the package:

  1. a language for music representation
  2. generation of music scores and audio files

Let’s start with a quick example.

Example

music <- 
  Music() +
  Meter(4, 4) +
  Line(c("C5", "D5", "E5", "F5"))
  
show(music)

Let’s go through the code line by line:

  1. music <- defines a variable.
  2. Music() initializes a Music object.
  3. + Meter(4, 4) adds a 4/4 time signature.
  4. + Line(c("C5", "D5", "E5", "F5")) adds four notes.
  5. show(music) generates the music.

We don’t have to stop here. We can add more components. For example, add a tempo:

music <- music + Tempo(180)
show(music)

Add an articulation:

music <- music + Articulation(">", 1)
show(music)

Add a pedal:

music <- music + Pedal(1, 4)
show(music)

There are still many other components we can add. Before we dive in, let’s see how to install and configure gm.

Installation

Install gm:

install.packages("gm")

Install MuseScore. MuseScore is open source and free notation software. Internally, gm uses MuseScore to generate music.

Configuration

You don’t need to configure anything if MuseScore is installed to a default path.

Otherwise, please specify the path to the MuseScore executable file in .Renviron file:

  1. Open .Renviron file.
  2. Add environment variable MUSESCORE_PATH=<path to MuseScore>.
  3. Restart R session.

For example, the environment variable is like

Music

At the highest level, gm uses Music objects to represent music.

As shown in the Example section, the workflow for creating music is usually like this:

  1. Initialize a Music object with Music().
  2. Add components to it with +.
  3. Display the music with show().

A Music object is represented as a list of data frames. Each data frame represents some type of components of the music. To examine a Music object’s representation, simply print it. For example, below is the representation of the Music object created in the Example section:

music <- 
  Music() +
  Meter(4, 4) +
  Line(c("C5", "D5", "E5", "F5"))

music
#> Music 
#> 
#> $meters
#> # A tibble: 1 × 6
#>     bar number  unit actual_number actual_unit invisible
#>   <int>  <int> <int>         <int>       <int> <lgl>    
#> 1     1      4     4             4           4 FALSE    
#> 
#> $notes
#> # A tibble: 4 × 7
#>    line     i     j pitch  midi duration length
#>   <int> <int> <int> <chr> <int> <chr>     <dbl>
#> 1     1     1    NA C5       72 <NA>          1
#> 2     1     2    NA D5       74 <NA>          1
#> 3     1     3    NA E5       76 <NA>          1
#> 4     1     4    NA F5       77 <NA>          1
#> 
#> $lines
#> # A tibble: 1 × 7
#>    part staff voice segment   bar offset name 
#>   <int> <int> <int>   <int> <int>  <dbl> <chr>
#> 1     1     1     1       1     1      0 <NA>

We will explore every type of components as we proceed.

Musical Line

Musical lines are the most basic units of music in gm. You can create a musical line with Line().

Pitches and Durations

You can specify the pitches and durations of a musical line with arguments pitches and durations. For example:

line <- Line(
  pitches = c("C5", "D5", "E5", "F5"),
  durations = c(0.5, 1, 1.5, 2)
)

music <- Music() + Meter(5, 4) + line
show(music, "score")

If pitches and durations have different lengths, the shorter one will be recycled. This feature is useful for repeating a pattern. For example:

line <- Line(
  pitches = c("A3", "E4", "C5", "B3", "E4", "G#4"),
  durations = c("quarter.", "eighth", "quarter")
)

music <- Music() + Meter(3, 4) + line
show(music)

We will talk more about pitches and durations later.

Musical Line Insertion

You can insert a musical line at positions other than the first beat of the first bar with arguments bar and offset. For example:

line <- Line(c("C5", "D5", "E5"), bar = 2, offset = 1)
music <- Music() + Meter(4, 4) + line
show(music, "score")

The musical line is inserted at the second beat of the second bar.

A more interesting example:

pitches <- c(64, 65, 69, 71, 72, 76)
music <- Music() + Meter(4, 4)

for (i in 0:8) {
  music <- music + Line(pitches, offset = 0.5 * i)
}

show(music)

The music contains nine parts, with identical pitches and durations. The difference lies in their insertion positions, creating an interesting echo sound effect.

Multiple Musical Lines

Music can contain more than one musical line. For example:

line_1 <- Line(c("C5", "D5", "E5", "F5"))
line_2 <- Line(c("E4", "G4"), 2)
line_3 <- Line("C4", 4)
music <- Music() + Meter(4, 4) + line_1 + line_2 + line_3
show(music, "score")

You can assign a name to a musical line with argument name. For example:

line_1 <- Line(c("C5", "D5", "E5", "F5"), name = "a")
line_2 <- Line(c("E4", "G4"), 2, name = "b")
line_3 <- Line("C4", 4, name = "c")
music <- Music() + Meter(4, 4) + line_1 + line_2 + line_3
show(music, "score")

By default, a musical line is added to the end. You can change it with argument to and after. For example:

line_1 <- Line(c("C5", "D5", "E5", "F5"), name = "a")
line_2 <- Line(c("E4", "G4"), 2, name = "b")
line_3 <- Line("C4", 4, name = "c", to = "a", after = FALSE)
music <- Music() + Meter(4, 4) + line_1 + line_2 + line_3
show(music, "score")

Now the third musical line is added to the beginning.

Musical Line Hierarchy

There is a hierarchy of musical lines:

  1. part
  2. staff
  3. voice
  4. segment

From the perspective of a music score,

You can specify the hierarchy with argument as. For example, the following music has two parts:

music <- 
  Music() +
  Meter(4, 4) +
  Line(c("C5", "D5", "E5", "F5")) +
  Line(c("C4", "G4"), 2, as = "part")

show(music, "score")

The following music has one part, and this part has two staffs:

music <- 
  Music() +
  Meter(4, 4) +
  Line(c("C5", "D5", "E5", "F5")) +
  Line(c("C4", "G4"), 2, as = "staff")

show(music, "score")

The following music has one part of one staff, and this staff has two voices:

music <- 
  Music() +
  Meter(4, 4) +
  Line(c("C5", "D5", "E5", "F5")) +
  Line(c("C4", "G4"), 2, as = "voice")

show(music, "score")

Actually, the three examples above all sound the same. The hierarchy of musical lines is only discernible from the score.

The following music has one part of one staff of one voice, and this voice has two segments:

music <- 
  Music() +
  Meter(4, 4) +
  Line(c("C5", "D5", "E5", "F5")) +
  Line(c("C4", "G4"), 2, as = "segment", bar = 2)

show(music, "score")

Segments are used to insert musical lines into other musical lines.

Musical Line Representation

The $lines data frame of a Music object contains the information about all musical lines. For example:

line_1 <- Line(c("C5", "D5", "E5", "F5"), name = "a")
line_2 <- Line(c("E4", "G4"), 2, name = "b", as = "voice")
line_3 <- Line("C4", 2, name = "c", as = "staff", offset = 2)
music <- Music() + Meter(4, 4) + line_1 + line_2 + line_3
show(music, "score")

music$lines
#> # A tibble: 3 × 7
#>    part staff voice segment   bar offset name 
#>   <int> <int> <int>   <int> <int>  <dbl> <chr>
#> 1     1     1     1       1     1      0 a    
#> 2     1     1     2       1     1      0 b    
#> 3     1     2     1       1     1      2 c

We can see from the data frame that this music has one part, and this part has two staffs. The first staff has two voices. The second staff is inserted at the third beat.

Pitch

Pitch Notation

You can use scientific pitch notations to specify pitches. For example:

pitches <- c("C4", "C#4", "D-4", "C##4", "D--4")
music <- Music() + Meter(5, 4) + Line(pitches)
show(music, "score")

A pitch notation consists of

  1. a tone name which can be C, D, E, F, G, A, or B,
  2. an optional accidental which can be -, --, #, or ##, and
  3. a number between 0 and 9 that indicates the pitch’s octave.

MIDI Note Number

A MIDI note number is a number between 12 and 127. You can also use MIDI note numbers to specify pitches. For example:

pitches <- 61:64
music <- Music() + Meter(4, 4) + Line(pitches)
show(music, "score")

See a conversion table of pitch notations and MIDI note numbers.

An advantage of MIDI note numbers is ease of operation. Suppose we have the following four MIDI note numbers:

pitches <- c(60, 62, 64, 65)
music <- Music() + Meter(4, 4) + Line(pitches)
show(music)

To transpose them up by a fifth, we can simply add 7 to them:

pitches <- pitches + 7
music <- Music() + Meter(4, 4) + Line(pitches)
show(music)

However, a disadvantage of MIDI note numbers is ambiguity. For example, there is more than one equivalent pitch notation to the MIDI note number 73:

pitches <- c("C#5", "D-5", "B##4")
music <- Music() + Meter(3, 4) + Line(pitches)
show(music, "score")

gm interprets MIDI note numbers based on keys and neighbor pitches. In the following example, MIDI note number 73 is interpreted as C#5 in the first measure, but as D-5 in the second measure:

pitches <- c(73, 73)
music <- Music() + Meter(1, 4) + Key(7) + Key(-7, bar = 2) + Line(pitches)
show(music, "score")

Rest

You can use NA to specify rests. For example:

pitches <- c("C4", NA, "D4", NA)
music <- Music() + Meter(4, 4) + Line(pitches)
show(music, "score")

Sometimes rests are added by gm automatically. In the following example, the musical line is inserted at the second bar, so a rest is added to the first bar:

pitches <- c("C4", NA, "D4", NA)
music <- Music() + Meter(4, 4) + Line(pitches, bar = 2)
show(music, "score")

In the following example, the musical line doesn’t fill the entire bar, so some rests are added to the end:

pitches <- "C4"
music <- Music() + Meter(4, 4) + Line(pitches)
show(music, "score")

Chord

You can use vectors to specify chords. Just remember to wrap them in a list. For example:

pitches <- list("C4", c("E4", "G4"), NA, c(64, 67))
music <- Music() + Meter(4, 4) + Line(pitches)
show(music)

Key

You can use Key() to add a key signature. All keys:

keys <- -7:7

for (key in keys) {
  print(Key(key))
}
#> Key C- Major (A- Minor) 
#> Key G- Major (E- Minor) 
#> Key D- Major (B- Minor) 
#> Key A- Major (F Minor) 
#> Key E- Major (C Minor) 
#> Key B- Major (G Minor) 
#> Key F Major (D Minor) 
#> Key C Major (A Minor) 
#> Key G Major (E Minor) 
#> Key D Major (B Minor) 
#> Key A Major (F# Minor) 
#> Key E Major (C# Minor) 
#> Key B Major (G# Minor) 
#> Key F# Major (D# Minor) 
#> Key C# Major (A# Minor)

You can add a key signature to a specific bar by using the bar argument. For example:

music <- Music() + Meter(1, 4) + Line(durations = rep(1, length(keys)))

for (i in seq_along(keys)) {
  music <- music + Key(keys[i], bar = i)
}

show(music, "score")

You can add a key signature only to a part or staff. For example:

music <-
  Music() +
  Meter(1, 4) +
  Line(NA) +
  Line(NA, as = "staff") +
  Line(NA) +
  Line(NA, as = "staff") +
  Key(1, to = 1) +
  Key(-1, to = 3, scope = "staff")

show(music, "score")

Key(1, to = 1) adds a G major to the first musical line and it affects the whole part, while Key(-1, to = 3, scope = "staff") adds an F major to the third musical line and it affects only that corresponding staff.

Clef

You can use Clef() to add a clef. For example:

clef <- Clef("F")
music <- Music() + Meter(2, 4) + Line(c("C3", "D3")) + clef
show(music, "score")

All supported clefs:

Clef("G")
#> Treble Clef

Clef("G", line = 1)
#> French Clef

Clef("G", octave = 1)
#> Octave Up Treble Clef

Clef("G", octave = -1)
#> Octave Down Treble Clef

Clef("F")
#> Bass Clef

Clef("F", line = 3)
#> Baritone F-Clef

Clef("F", line = 5)
#> Subbass Clef

Clef("F", octave = 1)
#> Octave Up Bass Clef

Clef("F", octave = -1)
#> Octave Down Bass Clef

Clef("C")
#> Alto Clef

Clef("C", line = 1)
#> Soprano Clef

Clef("C", line = 2)
#> Mezzo-Soprano Clef

Clef("C", line = 4)
#> Tenor Clef

Clef("C", line = 5)
#> Baritone C-Clef

You can add a clef at positions other than the first beat of the first bar. For example:

music <-
  Music() +
  Meter(2, 4) +
  Line(c("C3", "D3"), bar = 2) +
  Clef("F", bar = 2, offset = 1)

show(music, "score")

Rhythm

Duration Notation

You can use duration notations to specify durations. For example:

durations <- c("q.", "eighth", "quarter..", "16th")
music <- Music() + Meter(4, 4) + Line(durations = durations)
show(music, "score")

A duration notation can have two parts:

All supported duration types:

Duration Type Abbreviation Value
maxima m 32
long l 16
breve b 8
whole w 4
half h 2
quarter q 1
eighth 8 1/2
16th 16 1/4
32nd 32 1/8
64th 64 1/16
128th 128 1/32
256th 256 1/64
512th 512 1/128
1024th 1024 1/256

Duration Value

You can also use duration values to specify durations. For example:

durations <- 1:5
music <- Music() + Meter(5, 4) + Line("C4", durations)
show(music, "score")

A valid duration value should be equal to the total length of one or more duration types. For example, 0.5 is equal to the length of eighth; 1.5 is equal to the total length of quarter and eighth; 1.1 is not valid.

Tuplet

You can specify tuplets with more complex duration notations. For example:

durations <- rep("half/3", 3)
music <- Music() + Meter(2, 4) + Line(durations = durations)
show(music, "score")

Here we use “/3” to divide the half note into three tuplets. In the following example, we divide the half note into two tuplets of different lengths:

durations <- c("half/3", "half/3*(half/quarter)")
music <- Music() + Meter(2, 4) + Line(durations = durations)
show(music, "score")

Please note that tuplets should come in groups. For example, two half/3 tuplets or four half/3 tuplets cannot form a valid group. And a tuplet group should not cross bar lines.

gm can represent nested tuplets like this:

However, because gm uses MusicXML to represent music internally, but MuseScore cannot handle nested tuplets with MusicXML, we will skip this part.

Meter

You can use Meter() to add a time signature. For example:

music <-
  Music() +
  Meter(1, 4) +
  Meter(2, 4, bar = 2) +
  Line(c("C4", "E4", "G4"))

show(music, "score")

To apply show() to a Music object, there must be a time signature at the first bar.

You can add a pickup measure by using arguments actual_number, actual_unit, and invisible. For example:

music <-
  Music() +
  Meter(4, 4, actual_number = 1, actual_unit = 4) +
  Meter(4, 4, bar = 2, invisible = TRUE) +
  Line(c("A4", "B4", "C5", "D5", "E5"))

show(music, "score")

Tempo

You can use Tempo() to add a tempo mark. For example:

music <- 
  Music() +
  Meter(4, 4) +
  Line(60:67) +
  Tempo(90) +
  Tempo(120, bar = 2) +
  Tempo(200, bar = 2, offset = 2)

show(music)

You can add more sophisticated tempo marks by using argument marking. For example:

music <- 
  Music() +
  Meter(4, 4) +
  Line(60:67) +
  Tempo(90, marking = "Slowly!!") +
  Tempo(200, offset = 2, marking = "quarter. = quarter") +
  Tempo(120, bar = 2, marking = "Allegro (quarter = 110-130)")

show(music, "score")

Note

Note Representation

In Line(), we don’t provide notes, rests, or chords directly. Instead, we separate the pitches and durations. This feature is convenient for music programming.

However, Music objects have a data frame $notes for representing notes. For example:

music <-
  Music() +
  Meter(4, 4) +
  Line(list("C4", NA, c("C4", "E4")), list(1, "quarter", 2))

show(music, "score")

music$notes
#> # A tibble: 4 × 7
#>    line     i     j pitch  midi duration length
#>   <int> <int> <int> <chr> <int> <chr>     <dbl>
#> 1     1     1    NA C4       60 <NA>          1
#> 2     1     2    NA <NA>     NA quarter       1
#> 3     1     3     1 C4       60 <NA>          2
#> 4     1     3     2 E4       64 <NA>          2

The meaning of each column:

Tie

You can tie together two notes with Tie(). For example:

notes <- Line(c("C4", "C4"))
tie <- Tie(1)
music <- Music() + Meter(2, 4) + notes + tie
show(music, "score")

You can tie together some notes in chords or the whole chords. For example:

chords <- Line(list(c("C4", "E4"), c("C4", "E4")))
tie <- Tie(1, 2)
music <- Music() + Meter(2, 4) + chords + tie
show(music, "score")

chords <- Line(list(c("C4", "E4"), c("C4", "E4")))
tie <- Tie(1)
music <- Music() + Meter(2, 4) + chords + tie
show(music, "score")

gm automatically splits cross-bar notes and adds ties. For example:

cross_bar_note <- Line("C4", 4)
music <- Music() + Meter(1, 4) + cross_bar_note
show(music, "score")

Grace Note

You can add grace notes with Grace(). For example:

line <- Line(c("A3", "B3", "C4", "D4"), c(0.25, 0.25, 1, 1))
music <- Music() + Meter(2, 4) + line + Grace(1) + Grace(2)
show(music)

The first two notes are indicated as grace notes.

Accidental

gm adds accidentals to notes based on the key. You can change default accidentals with Accidental(). For example:

accidental <- Accidental("natural", 2)
music <- Music() + Meter(2, 4) + Line(c("C4", "C4")) + accidental
show(music, "score")

Notehead

You can change the shape or color of a note with Notehead(). For example:

notehead <- Notehead(1, shape = "diamond", color = "#FF0000")
music <- Music() + Meter(2, 4) + Line(c("C4", "C4")) + notehead
show(music, "score")

Stem

You can change the stem of a note with Stem(). For example:

stem <- Stem("none", 1)
music <- Music() + Meter(2, 4) + Line(c("C4", "C4")) + stem
show(music, "score")

Articulation

You can add an articulation to a note with Articulation(). Some examples:

music <- 
  Music() +
  Meter(4, 4) +
  Line(c("C4", "D4", "E4", "F4")) +
  Articulation(">", 1) +
  Articulation(".", 2) +
  Articulation("'", 3) +
  Articulation("-", 4)
  
show(music)

All supported articulations:

MuseScore MusicXML Symbol
accent accent >
staccato staccato .
staccatissimo staccatissimo
tenuto tenuto -
tenuto-staccato detached-legato -.
marcato strong-accent ^
scoop scoop NA
plop plop NA
doit doit NA
fall falloff NA
stress stress ,
unstress unstress u
soft accent soft-accent <>

Fermata

You can add a fermata to a note with Fermata(). For example:

music <- 
  Music() +
  Meter(4, 4) +
  Line(c("C4", "D4", "E4", "F4")) +
  Fermata(4, shape = "square")
  
show(music, "score")

Breath Mark

You can add a breath mark to a note with Breath(). For example:

music <- 
  Music() +
  Meter(4, 4) +
  Line(c("C4", "D4", "E4", "F4")) +
  Breath(2)
  
show(music, "score")

Ornament

You can add an ornament to a note with the following options:

Some examples:

music <- 
  Music() +
  Meter(7, 4) +
  Line(c("B4", "C5", "D5", "E5", "F5", "G5", "A5")) +
  Tremolo(3, 1) +
  Turn(2, inverted = TRUE) +
  Mordent(3, long = TRUE) + 
  Trill(4) + 
  Trill(5, 6) +
  Schleifer(7)

show(music)

Slur

You can add a slur to notes with Slur(). For example:

music <- Music() + Meter(4, 4) + Line(60:63) + Slur(1, 4)
show(music, "score")

Slurs can cross staffs. For example:

music <- 
  Music() +
  Meter(4, 4) +
  Line(c("C4", "D4")) +
  Line(c("E4", "F4"), offset = 2, as = "staff") +
  Slur(1, 2, to = 1, to_j = 2)

show(music, "score")

Loudness

Dynamics

You can use Dynamic() to add dynamic markings. For example:

music <-
  Music() +
  Meter(4, 4) +
  Line(60:67) +
  Dynamic("p", 1) +
  Dynamic("mf", 3) +
  Dynamic("fff", 5) +
  Dynamic("pp", 7)

show(music)

Velocity

You can use Velocity() to adjust note loudness more precisely. For example:

pitches <- 74:50
velocities <- seq(5, 127, 5)
music <- Music() + Meter(4, 4) + Tempo(200) + Line(pitches, 0.5)

for (i in seq_along(velocities)) {
  music <- music + Velocity(velocities[i], 1, i)
}

show(music)

Dynamic Change

You can use Hairpin() to add crescendo and decrescendo symbols. For example:

music <-
  Music() +
  Meter(4, 4) +
  Line(60:67) +
  Dynamic("pp", 1) +
  Dynamic("ff", 8) +
  Hairpin("<", 2, 7)

show(music)

Timbre

Instrument

You can use Instrument() to specify the instrument of a part. For example:

pitches <- c("C4", "D4", "E4", "F4")

music <-
  Music() +
  Meter(4, 4) +
  Line(pitches) +
  Line(pitches, bar = 2)

show(music)  

instrument_1 <- Instrument(10, 1)
instrument_1
#> Glockenspiel 
#> 
#> * to be added to the part containing Line 1
instrument_2 <- Instrument(76, 2)
instrument_2
#> Pan Flute 
#> 
#> * to be added to the part containing Line 2
music <- music + instrument_1 + instrument_2
show(music)

You can change the sound placement with argument pan. For example:

pitches <- c("C4", "D4", "E4", "F4")

music <-
  Music() +
  Meter(4, 4) +
  Line(pitches) +
  Line(pitches, bar = 2) + 
  Line(pitches, bar = 3) +
  Instrument(1, 1, pan = -90) +
  Instrument(1, 2, pan = 0) +
  Instrument(1, 3, pan = 90)

show(music)  

Unfortunately, pan only works in MuseScore 3, while the latest version is 4, as of the time of writing. The two versions have slightly different features. So it’s recommended to install both versions, and temporarily change the path using options() when needed. For example, in macOS:

path <- "/Applications/MuseScore 3.app/Contents/MacOS/mscore"
options(gm.musescore_path = path)

Pedal

You can use Pedal() to add pedals. For example, the following are first two bars of Chopin’s nocturne Op.72 No.1:

music <-
  Music() +
  Meter(4, 4) +
  Key(1) +
  Tempo(60) +
  
  Line(c("B4", "G5", "F#5", "E5"), c("8", "h.", "8", "8"), bar = 2) +
  Grace(1) +
  
  Line(rep(c("E2", "B2", "G3", "E3", "C4", "B3"), 4), "q/3", as = "staff") +
  Clef("F") +
  Dynamic("p", 1) +
  Pedal(1, 12) +
  Pedal(13, 24)

show(music)

Lyrics

You can add lyrics by using Lyric(). For example:

music <-
  Music() +
  Meter(4, 4) +
  Line(60:63) +
  Lyric("My_love-", 1) +
  Lyric("-ly", 2) +
  Lyric("cat_", 3) +
  Lyric("_", 4) +
  Lyric("Hey", 1, verse = 2) +
  Lyric("Jude", 2, verse = 2)
  
show(music, "score")

MuseScore

You can pass MuseScore command line options to export() and show().

For example, you can change score’s DPI and margin:

music <- Music() + Meter(4, 4) + Line("C5")
show(music, "score", musescore = "-r 400 -T 100")