--- title: "Complete Guide to gm" output: rmarkdown::html_vignette: toc: true vignette: > %\VignetteIndexEntry{Complete Guide to gm} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE, message = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", # To prevent CRAN from building this vignette: # # 1. Add `BUILD_VIGNETTE=TRUE` in .Renviron before calling # `devtools::build()` or `R CMD build`, to build this vignette locally. # # 2. Remove `BUILD_VIGNETTE=TRUE` before calling `R CMD check --as-cran`. eval = Sys.getenv("BUILD_VIGNETTE") == "TRUE" ) ``` ```{r message = FALSE} 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 ```{r} 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: ```{r} music <- music + Tempo(180) show(music) ``` Add an articulation: ```{r} music <- music + Articulation(">", 1) show(music) ``` Add a pedal: ```{r} 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: ```r install.packages("gm") ``` Install [MuseScore](https://musescore.org/). 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](https://musescore.org/en/handbook/4/revert-factory-settings). Otherwise, please specify the path to the MuseScore executable file in .Renviron file: 1. Open .Renviron file. 2. Add environment variable `MUSESCORE_PATH=`. 3. Restart R session. For example, the environment variable is like - `MUSESCORE_PATH=C:/Program Files/MuseScore 4/bin/MuseScore4.exe` in Windows, and - `MUSESCORE_PATH=/Applications/MuseScore\ 4.app/Contents/MacOS/mscore` in macOS. ## 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: ```{r} music <- Music() + Meter(4, 4) + Line(c("C5", "D5", "E5", "F5")) music ``` 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: ```{r} 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: ```{r} 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: ```{r} 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: ```{r} 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: ```{r} 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: ```{r} 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: ```{r} 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, - a part can have several staffs; - a staff can have up to four voices; - a voice can have several segments. You can specify the hierarchy with argument `as`. For example, the following music has two parts: ```{r} 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: ```{r} 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: ```{r} 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: ```{r} 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: ```{r} 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") ``` ```{r} music$lines ``` 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](https://en.wikipedia.org/wiki/Scientific_pitch_notation) to specify pitches. For example: ```{r} 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: ```{r} pitches <- 61:64 music <- Music() + Meter(4, 4) + Line(pitches) show(music, "score") ``` See a [conversion table](https://en.wikipedia.org/wiki/Scientific_pitch_notation#Table_of_note_frequencies) 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: ```{r} 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: ```{r} 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: ```{r} 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: ```{r} 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: ```{r} 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: ```{r} 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: ```{r} 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: ```{r} 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: ```{r} keys <- -7:7 for (key in keys) { print(Key(key)) } ``` You can add a key signature to a specific bar by using the `bar` argument. For example: ```{r} 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: ```{r} 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: ```{r} clef <- Clef("F") music <- Music() + Meter(2, 4) + Line(c("C3", "D3")) + clef show(music, "score") ``` All supported clefs: ```{r} Clef("G") Clef("G", line = 1) Clef("G", octave = 1) Clef("G", octave = -1) Clef("F") Clef("F", line = 3) Clef("F", line = 5) Clef("F", octave = 1) Clef("F", octave = -1) Clef("C") Clef("C", line = 1) Clef("C", line = 2) Clef("C", line = 4) Clef("C", line = 5) ``` You can add a clef at positions other than the first beat of the first bar. For example: ```{r} 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: ```{r} durations <- c("q.", "eighth", "quarter..", "16th") music <- Music() + Meter(4, 4) + Line(durations = durations) show(music, "score") ``` A duration notation can have two parts: - a duration type or its abbreviation - zero to four dots All supported duration types: ```{r echo = FALSE} duration_types <- gm:::duration_types duration_types$value <- duration_types$value |> lapply(gm:::to_fraction) |> lapply(\(fraction) { if (fraction[2] == 1) fraction[1] else paste(fraction, collapse = "/") }) |> as.character() names(duration_types) <- c("Duration Type", "Abbreviation", "Value") knitr::kable(duration_types) ``` ### Duration Value You can also use duration values to specify durations. For example: ```{r} 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: ```{r} 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: ```{r} 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: ![](../man/figures/nested_tuplets.png) 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: ```{r} 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: ```{r} 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: ```{r} 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: ```{r} 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: ```{r} music <- Music() + Meter(4, 4) + Line(list("C4", NA, c("C4", "E4")), list(1, "quarter", 2)) show(music, "score") ``` ```{r} music$notes ``` The meaning of each column: - `line` indicates the row number of the musical line a note belongs to. - `i` indicates a note's index in the musical line. - `j` indicates a note's index in a chord. - `pitch` indicates the pitch notation of a note. - `midi` indicates the MIDI note number of a note. - `duration` indicates the duration notation of a note. - `length` indicates the length of a note's duration. ### Tie You can tie together two notes with `Tie()`. For example: ```{r} 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: ```{r} chords <- Line(list(c("C4", "E4"), c("C4", "E4"))) tie <- Tie(1, 2) music <- Music() + Meter(2, 4) + chords + tie show(music, "score") ``` ```{r} 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: ```{r} 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: ```{r} 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: ```{r} 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: ```{r} 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: ```{r} 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: ```{r} music <- Music() + Meter(4, 4) + Line(c("C4", "D4", "E4", "F4")) + Articulation(">", 1) + Articulation(".", 2) + Articulation("'", 3) + Articulation("-", 4) show(music) ``` All supported articulations: ```{r echo = FALSE} articulations <- gm:::articulations names(articulations) <- c("MuseScore", "MusicXML", "Symbol") knitr::kable(articulations) ``` ### Fermata You can add a fermata to a note with `Fermata()`. For example: ```{r} 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: ```{r} 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: - `Schleifer()` - `Mordent()` - `Trill()` - `Turn()` - `Tremolo()` Some examples: ```{r} 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: ```{r} music <- Music() + Meter(4, 4) + Line(60:63) + Slur(1, 4) show(music, "score") ``` Slurs can cross staffs. For example: ```{r} 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: ```{r} 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: ```{r} 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: ```{r} 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: ```{r} pitches <- c("C4", "D4", "E4", "F4") music <- Music() + Meter(4, 4) + Line(pitches) + Line(pitches, bar = 2) show(music) ``` ```{r} instrument_1 <- Instrument(10, 1) instrument_1 ``` ```{r} instrument_2 <- Instrument(76, 2) instrument_2 ``` ```{r} music <- music + instrument_1 + instrument_2 show(music) ``` You can change the sound placement with argument `pan`. For example: ```{r echo = FALSE} path <- "/Applications/MuseScore 3.app/Contents/MacOS/mscore" options(gm.musescore_path = path) ``` ```{r} 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) ``` ```{r, echo = FALSE} options(gm.musescore_path = NULL) ``` 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: ```r 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: ```{r} 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: ```{r} 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](https://musescore.org/en/handbook/4/command-line-usage) to `export()` and `show()`. For example, you can change score's DPI and margin: ```{r} music <- Music() + Meter(4, 4) + Line("C5") show(music, "score", musescore = "-r 400 -T 100") ```