--- title: "Musical scales" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Musical scales} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include = FALSE} options(crayon.enabled = TRUE) sgr_wrap <- function(x, options){ paste0("
", fansi::sgr_to_html(x = htmltools::htmlEscape(x)), "
")
}
knitr::knit_hooks$set(output = sgr_wrap)
knitr::opts_chunk$set(
collapse = TRUE, comment = "#>", message = FALSE, warning = FALSE, error = FALSE, tidy = FALSE, out.width = "100%"
)
library(tabr)
library(dplyr)
mainIntervals <- tbl_df(mainIntervals)
```
Programming with musical scales can be assisted by a collection of functions in `tabr` that check and manipulate musical scales, modes and key signatures.
## Key signatures
For key signatures, `keys` lists valid key signature abbreviation strings for each key as used in `tabr`. There are several `key_is_*` functions that return a logical result regarding properties of keys:
* `key_is_natural()`
* `key_is_sharp()`
* `key_is_flat()`
* `key_is_major()`
* `key_is_minor()`
There are also the functions, `key_n_flats()` and `key_n_sharps()`, that give the number of respective accidentals in a key signature.
```{r key}
keys()
key_is_flat("f")
key_n_flats("f")
```
The previous section gave an overview of noteworthy strings. While some of the functions that help enforce proper notation in R seemed like they did not offer much utility in terms of direct use, it was clear that they were integral to the robustness of other `tabr` functions. Now, looking at these key signature helpers, it may be tempting to dismiss their utility even more quickly because a trained musician does not need to invoke them at the command line to know what result they will return.
These functions are not provided to answer basic questions in an interactive R session so much as they are for programming. These are some of the initial building blocks on top of which more complex functions are built, including many functions in `tabr`. As the collection of music programming helper functions in `tabr` grows, it becomes easy to do more with less.
## Scales
Several predefined musical scales are provided and accessible by calling various `scale_*` functions.
* scale_chromatic
* scale_diatonic
* scale_minor
* scale_major
* scale_harmonic_minor
* scale_hungarian_minor
* scale_melodic_minor
* scale_jazz_minor
```{r scales}
scale_hungarian_minor(key = "am", collapse = TRUE)
```
You can specify whether the vector result should be returned as is, for convenient vectorized programming pipelines, or collapsed to a single string in keeping with the space-delimited time syntax format common throughout `tabr`. Many functions in `tabr` accept both formats as inputs and/or offer them as outputs.
You can also specify if octave numbering should be included or stripped. Octave numbering is included by default because this maintains pitch order when the scale does not start on `C`. Every note in a noteworthy string has an implicit octave-3 (the octave below middle C, `C4`) position if not explicitly stated. Octave numbering attempts to be somewhat balanced around `C3`. If the result is not what is desired, it can be shifted by 12 semitones with `transpose()`.
```{r scales2}
scale_major("f", TRUE, ignore_octave = TRUE)
scale_major("f", TRUE, ignore_octave = FALSE)
```
See `help("scale-helpers")` for details. Depending on the scale, other arguments are available.
## Modes
The seven modern modes are also available with `mode_*` functions or through `modern_mode()` by passing the mode string name. Other functions include `is_mode` and `rotate_mode`.
* `mode_ionian()`
* `mode_dorian()`
* `mode_phrygian()`
* `mode_lydian()`
* `mode_mixolydian()`
* `mode_aeolian()`
* `mode_locrian()`
```{r modes}
modes()
mode_aeolian("c")
```
## Diatonic scale chords
The `scale_chords()` function provides a list of diatonic scale chords based on the root note and scale chosen. It returns triads by default and can also return seventh chords.
```{r scale_chords}
scale_chords("b_", "major", "seventh", collapse = TRUE)
scale_chords("f#", "minor", "triad", collapse = TRUE)
```
## Scale degrees
The functions `scale_note()` and `scale_degree()` map between notes and degree in a given scale. For chords in a noteworthy string, only the root note is considered. For `scale_degree()`, if a note is not diatonic to the scale, `NA` is returned. `NA` is also used when rests occur. Octaves are ignored. For `scale_note()`, degrees outside the range of the scale are recycled. See below. To see if chords are fully diatonic, use `is_diatonic()` or the more general `is_in_scale()`. `chord_degree()` will return a list comparable to `scale_degree()`.
```{r scale_degrees}
x <- "c e gb'd'"
scale_degree(x)
scale_degree(x, key = "a")
scale_degree(x, key = "am")
scale_degree(x, scale = "chromatic")
scale_note(1:7, "d")
scale_note(c(1:8), "dm", "harmonic minor")
note_in_scale("a_ g#", "a_", strict_accidentals = FALSE)
x <- "r d dfa df#a f#ac#"
chord_degree(x, "d")
is_in_scale(x, "d")
is_diatonic(x, "d")
```
Other functions in `tabr` also work with scales and some build upon functions introduced here.
## Musical intervals
Musical intervals can be referenced by a name or a numeric value defining the separation of two notes. `interval_semitones()` returns a positive integer describing the number of semitones spanned by a common interval. It takes a name or abbreviation of a common interval as input. Essentially, any entry from any other column in the `mainIntervals` dataset can be used to obtain the interval in semitones. It's a simple filter and match, but it is convenient to map between different representations of the same property in `tabr` without typing the extra bit of code to do so each time.
```{r intervals}
mainIntervals
interval_semitones(c("m3", "M7"))
```
Likely more useful for programming, the function `pitch_interval()` provides the number of semitones between two input notes. This function does not relate to specific scales, but is worth mentioning on the topic of interval helpers. It provides both magnitude and direction. The result is negative if the first note is of higher pitch than the second. It is vectorized and both inputs must have the same number of timesteps.
Chords can be reduced to their root note (lowest pitch) for comparison or forced to yield an `NA` interval with respect to its two adjacent timesteps (e.g., `c` to `ceg` is `NA` and so is `ceg` to `c`). Rests `r` and silent rests `s` also yield an `NA` for the interval from a prior note.
```{r intervals2}
pitch_interval("a2", "c")
pitch_interval("c d e", "c c c")
pitch_interval("r c ceg c e g s", "a c d d f# a e")
pitch_interval("r c ceg c e g s", "a c d d f# a e", use_root = FALSE)
```
Next is `scale_interval()`. This function is similar to `pitch_interval()` in that it takes two noteworthy strings, which together define an intervals element-wise. It is almost an inverse of `interval_semitones()` except that you provide notes rather than the semitone distance of their intervals. The function returns a main interval name or abbreviation from `mainIntervals`, depending on `format`. The results are name-only. They are not signed. Use `pitch_interval()` to obtain direction.
```{r intervals3}
scale_interval("c c c c", "c, e g b")
scale_interval("a2", "c", format = "mmp")
```
There are also lagged difference versions of these functions. You can adjust the lag with `n`. You can also retain (convenient for data frames) or trim the `n` leading `NA`s. This does not trim meaningful `NA`s like those resulting from rests.
```{r intervals4}
pitch_diff("c d e f g a b")
pitch_diff("c d e f g a b", trim = TRUE)
scale_diff("c d e f g a b")
scale_diff("c d e f g a b", n = 2)
```
Lagged intervals respect rest timesteps. All timestep positions including rests are retained, but the lag-`n` difference computation ignores them.
```{r intervals5}
x <- "a, c r r r r g"
pitch_diff(x)
scale_diff(x)
pitch_diff(x, n = 2)
scale_diff(x, n = 2, trim = TRUE)
```