Skip to content

Commit

Permalink
Created harte() function.
Browse files Browse the repository at this point in the history
  • Loading branch information
ncondits3 committed Dec 27, 2023
1 parent 8c2271f commit 96f9c84
Show file tree
Hide file tree
Showing 147 changed files with 512 additions and 311 deletions.
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: humdrumR
Title: humdrumR
Version: 0.7.0.1
Version: 0.7.0.2
Authors@R: c(person("Nathaniel", "Condit-Schultz", email = "[email protected]", role = c("aut", "cre")),
person("Claire", "Arthur", email = "[email protected]", role = "aut"))
Description: This package is a toolkit for the visualization, manipulation, and analysis of data encoded in the Humdrum syntax (<http://www.humdrum.org).
Expand Down
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,7 @@ export(grid)
export(group_by)
export(groups)
export(harm)
export(harte)
export(helmholtz)
export(hint)
export(hop)
Expand Down
147 changes: 133 additions & 14 deletions R/Chords.R
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,7 @@ tset2tonalHarmony <- function(x,
root.case = TRUE,
Key = NULL, keyed = FALSE,
inversion.labels = NULL,
sep = '', ...) {
collapse = TRUE, sep = '', ...) {
Key <- diatonicSet(Key)

if (keyed && !is.null(Key)) {
Expand Down Expand Up @@ -507,9 +507,12 @@ tset2tonalHarmony <- function(x,

inversion.label <- if (!is.null(inversion.labels)) getInversion(x, inversion.labels = inversion.labels)

tonalharmony <- pasteordered(parts, root = root, quality = quality, figuration = figuration, inversion = inversion.label, bass = bass, sep = sep)
if (collapse) {
pasteordered(parts, root = root, quality = quality, figuration = figuration, inversion = inversion.label, bass = bass, sep = sep)
} else {
list(root = root, quality = quality, figuration = figuration, inversion = inversion.label, bass = bass)[parts]
}

tonalharmony
}


Expand Down Expand Up @@ -629,6 +632,49 @@ tset2chord <- function(x, figurationArgs = c(), major = NULL, ...) {

}

tset2harte <- function(x, Key = NULL, figurationArgs = list(), flat = '-', ...) {

Key <- diatonicSet(Key)

if (!is.null(Key)) {
Key <- rep(Key, length.out = length(x))
x[!is.na(Key)] <- x[!is.na(Key)] + getRoot(Key[!is.na(Key)])
}

root <- getRootTint(x)

qualities <- tset2alterations(x, flat = 'b', inversion = FALSE)
extensions <- tset2extensions(x, inversion = FALSE, inverted = x@Inversion > 0)
extensions <- array(.paste(qualities, extensions), dim = dim(qualities))

fig <- apply(extensions, 1, .paste, collapse = ',')

fig <- paste0('(', fig, ')')

shorthand <- c('3,5' = 'maj', 'b3,5' = 'min', 'b3,b5' = 'dim', '3,#5' = 'aug',
'3,5,7' = 'maj7', 'b3,5,b7' = 'min7', '3,5,b7' = '7', 'b3,b5,b7' = 'hdim7', 'b3,b5,bb7' = 'dim7', 'b3,5,7' = 'minmaj7',
'3,5,6' = 'maj6', 'b3,5,6' = 'min6',
'3,5,7,9' = 'maj9', 'b3,5,b7,9' = 'min9', '3,5,b7,9' = '9',
'5,11' = 'sus4', '5,9' = 'sus2')
names(shorthand) <- paste0('(1,', names(shorthand), ')')
fig[fig %in% names(shorthand)] <- shorthand[fig[fig %in% names(shorthand)]]



root <- tint2simplepitch(root, flat = flat, ...)

# bass
bass <- local({
inverted <- x@Inversion > 0L
bass <- character(length(x))
bass[inverted] <- paste0('/', extensions[cbind(which(inverted), x@Inversion[inverted] + 1L)])
bass

})
paste0(root, ':', fig, bass)

}




Expand Down Expand Up @@ -1018,7 +1064,6 @@ sciQualities2tset <- function(str, inversion = 0L, ...) {

dset <- qualities2dset(chord, steporder = 4L, allow_partial = TRUE, ...)


extension <- sapply(stringr::str_locate_all(str, '[^.]'), \(x) sum(as.integer(2L^(x[, 'start'] - 1L))))

tset(dset@Root, dset@Signature, dset@Alteration, extension = extension, inversion = inversion)
Expand Down Expand Up @@ -1059,19 +1104,19 @@ figuredBass2tset <- function(x, ...) {

figurations <- parseFiguration(figurations)

tsets <- .unlist(figurations[,
tset <- .unlist(figurations[,
{
tints <- interval2tint(paste0(Accidentals[[1]], Degrees[[1]]), qualities = FALSE)
tints <- tints - tints[1]
inversion <- Inversion

sciDegrees <- paste(tint2specifier(tints, qualities=T, explicitNaturals = TRUE), collapse = '')
list(list(sciQualities2tset(sciDegrees, minor = 'm', diminish = 'd', augment = 'A', major = 'M',
inversion = inversion) - tints[inversion + 1L]))
inversion = inversion) - tints[inversion + 1L]))
},
by = 1:nrow(figurations)]$V1)

tsets + bass
tset + bass


}
Expand Down Expand Up @@ -1121,7 +1166,6 @@ chord2tset <- function(x, ..., major = 'maj', minor = 'min', augment = 'aug', di

if (any(bass != '')) {
hasbass <- bass != ''
bassint <- integer(sum(hasbass))

bassint <- getFifth(kern2tint(stringr::str_sub(bass[hasbass], start = 2L))) - getFifth(kern2tint(tonalChroma[hasbass]))
tset@Inversion[hasbass] <- c(0L, 2L, 4L, 6L, 1L, 3L, 5L)[bassint %% 7L + 1L]
Expand All @@ -1130,9 +1174,69 @@ chord2tset <- function(x, ..., major = 'maj', minor = 'min', augment = 'aug', di

tset

}

harte2tset <- function(x, ..., major = 'maj', minor = 'min', augment = 'aug', diminish = 'dim', flat = '-') {
REparse(x,
makeRE.harte(..., major = major, minor = minor, augment = augment, diminish = diminish,
flat = flat, collapse = FALSE), # makes tonalChroma, figqual, bass
toEnv = TRUE) -> parsed

# shorthand translation
shorthands <- local({
shorthands <- c(maj = '3,5', min = 'b3,5', aug = '3,#5', dim = 'b3,b5',
'7' = '3,5,b7', maj7 = '3,5,7', min7 = 'b3,5,b7', dim7 = 'b3,b5,bb7', hdim7 = 'b3,b5,b7', minmaj7 = 'b3,5,7',
maj6 = '3,5,6', min6 = 'b3,5,6',
'9' = '3,5,b7,9', maj9 = '3,5,7,9', min9 = 'b3,5,b7,9',
sus2 = '2,5', sus4 = '4,5')
shorthands <- setNames(paste0('(1,', shorthands, ')'), names(shorthands))

names(shorthands) <- gsub('maj', major, names(shorthands))
names(shorthands) <- gsub('min', minor, names(shorthands))
names(shorthands) <- gsub('aug', augment, names(shorthands))
names(shorthands) <- gsub('dim', diminish, names(shorthands))

shorthands


})


fig <- figqual
fig[!grepl('^\\(', figqual)] <- shorthands[figqual[!grepl('^\\(', figqual)]]

# translate fig to tertian
fig <- gsub('^\\(1,', '', gsub('\\)$', '', fig))
fig <- strsplit(fig, split = ',')
tertian <- c('P', rep('.', 6))
ind <- c('3' = 2L, '5' = 3L, '7' = 4L,
'2' = 5L, '9' = 5L,
'4' = 6L, '11' = 6L,
'6' = 7L, '13' = 7L)
tertian <- sapply(unique(fig),
\(fig) {
qualities <- tint2specifier(interval2tint(fig, qualities = FALSE, flat = 'b'), qualities = TRUE, explicitNaturals = TRUE)

fig <- gsub('[b#]+', '', fig)
tertian[ind[fig]] <- qualities
paste(tertian, collapse = '')

})


tset <- sciQualities2tset(tertian, augment = 'A', diminish = 'd')[match(fig, unique(fig))]

if (any(bass != '')) {
hasbass <- bass != ''

tset@Inversion[hasbass] <- ind[gsub('\\/b?', '', bass[hasbass])] - 1L

}

tset + kern2tint(tonalChroma, flat = flat)

}

##... Numbers

integer2tset <- function(x) tset(x, x)
Expand Down Expand Up @@ -1208,6 +1312,7 @@ tertianSet.integer <- integer2tset
tertianSet.character <- makeHumdrumDispatcher(list('harm', makeRE.harm, harm2tset),
list('roman', makeRE.roman, roman2tset),
list('figuredBass', makeRE.figuredBass, figuredBass2tset),
list('harte', makeRE.harte, harte2tset),
list('any', makeRE.tertian, tertian2tset),
list('any', makeRE.chord, chord2tset),
funcName = 'tertianSet.character',
Expand Down Expand Up @@ -1268,11 +1373,15 @@ setAs('tertianSet', 'diatonicSet', function(from) tset(from@Root, from@Signature
#' These functions can be used to extract and "translate," or otherwise modify, data representing tertian harmony information.
#' The functions are:
#'
#' + [chord()]
#' + [figuredBass()]
#' + [harm()]
#' + [roman()]
#' + [tertian()]
#' + Jazz/Pop
#' + [chord()]
#' + [harte()]
#' + Classical
#' + [figuredBass()]
#' + [tertian()]
#' + *Roman Numerals*
#' + [harm()]
#' + [roman()]
#'
#' @seealso To better understand how these functions work, read about how tertian harmonies are
#' [parsed][chordParsing] and [deparsed][chordDeparsing].
Expand Down Expand Up @@ -1403,7 +1512,13 @@ NULL

#' "Pop/Jazz" chord symbols
#'
#' This function outputs a generic "jazz" chord symbol representation of a tonal harmony.
#' These functions outputs jazz/pop-style chord symbols.
#' There is no universal standard for how to notate such chord symbols, in particular in plain text.
#' The `chord()` function outputs a chord symbol representation roughly consistent with "standard practices."
#'
#' For more rigorous, consistent work, we recommend the [Harte](https://github.com/Computational-Cognitive-Musicology-Lab/Star-Wars-Thematic-Corpus) notation,
#' which is the standard used by MIREX, etc.
#' The `harte()` function will output standard Harte symbols.
#'
#' @examples
#' romanNumerals <- c('2I', '2IV7', '1V', '2vi', '2-VI', '2iio7', '2Vb9')
Expand All @@ -1420,6 +1535,10 @@ NULL
#' @export
chord <- makeChordTransformer(tset2chord, 'chord')

#' @rdname chord
#' @export
harte <- makeChordTransformer(tset2harte, 'harte')

#' Figured bass representation of harmony
#'
#' This function outputs a [figured bass](https://en.wikipedia.org/wiki/Figured_bass)
Expand Down
7 changes: 5 additions & 2 deletions R/Keys.R
Original file line number Diff line number Diff line change
Expand Up @@ -658,7 +658,7 @@ qualities2dset <- function(x, steporder = 2L, allow_partial = FALSE,
modes <- list(c(perfect, perfect, major, major, major, major, augment),
c(perfect, perfect, major, major, major, major, perfect),
c(perfect, perfect, major, major, major, minor, perfect),
c(perfect, perfect, major, minor, major, minor, perfect),
c(perfect, perfect, major, major, minor, minor, perfect),
c(perfect, perfect, major, minor, minor, minor, perfect),
c(perfect, perfect, minor, minor, minor, minor, perfect),
c(perfect, diminish, minor, minor, minor, minor, perfect))
Expand Down Expand Up @@ -709,10 +709,13 @@ qualities2dset <- function(x, steporder = 2L, allow_partial = FALSE,
change <- ifelse(which(altered) %in% c(1L, 2L, 7L), # Perfects
match(actual, quality.labels[-c(2, 4)]) - match(supposedtobe, quality.labels[-c(2, 4)]), # no M or m
match(actual, quality.labels[-3]) - match(supposedtobe, quality.labels[-3])) # no P

if (any(abs(change) > 1L)) change <- sign(change)

altermat <- matrix(0L, nrow = 1, ncol = 7)
altermat[((which(altered) - mode) %% 7L) + 1L] <- change
# altermat[ , ((which(altered) - mode - 1L) %% 7L) + 1L] <- change
altered <- which(altered)
altermat[ , (altered %% 7L) + 1L] <- change

alterint <- baltern2int(altermat)
c(mode = mode, altered = alterint)
Expand Down
43 changes: 40 additions & 3 deletions R/Regex.R
Original file line number Diff line number Diff line change
Expand Up @@ -543,7 +543,7 @@ makeRE.pc <- function(ten = 'A', eleven = 'B', ...) {

#### REs for diatonic sets ####

makeRE.alterations <- function(..., qualities = FALSE) {
makeRE.alterations <- function(..., qualities = FALSE, sep = '') {
# names(alteration.labels) <- gsub('augment', 'sharp', names(alteration.labels))
# names(alteration.labels) <- gsub('diminish', 'flat', names(alteration.labels))

Expand All @@ -552,8 +552,8 @@ makeRE.alterations <- function(..., qualities = FALSE) {
parts = c("species", "step"), step.signed = FALSE, flat = 'b',
step.labels = c(1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13),
regexname = 'alterations')

paste0('(', makeRE(..., qualities = qualities, quality.required = FALSE), ')*')
if (sep != '') sep <- paste0(sep, '?')
paste0('(', makeRE(..., qualities = qualities, quality.required = FALSE), sep, ')*')
}

makeRE.key <- function(..., parts = c("step", "species", "mode", "alterations"),
Expand Down Expand Up @@ -683,6 +683,43 @@ makeRE.chord <- function(..., major = '[Mm]aj', minor = 'min', augment = 'aug',

}

makeRE.harte <- function(..., major = 'maj', minor = 'min', augment = 'aug', diminish = 'dim',
bass.sep = '/', flat = '-',
collapse = TRUE) {
REs <- makeRE.tonalChroma(parts = c("step", "species"), flat = flat,
step.labels = '[A-G]', qualities = FALSE,
step.sign = FALSE, ...)

REs$colon <- ':'


triads <- c(major, minor, augment, diminish)
sevenths <- paste0(c(major, minor, diminish, paste0('h', diminish), paste0(minor, major), ''),
'7')
sixths <- paste0(c(major, minor), '6')
ninths <- paste0(c(major, minor, ''), 9)
sus <- c('sus2', 'sus4')

quality <-captureRE(c(triads, sevenths,sixths, ninths, sus))

figurations <- paste0('\\(', makeRE.alterations(..., step.labels = 1:13, flat = 'b', sep = ','), '\\)')

REs$figqual <- paste0('(', quality, '|', figurations, ')')

REs$bass <- paste0('(', bass.sep,
makeRE.tonalChroma(parts = c("species", "step"), flat = 'b',
step.labels = 1:13, qualities = FALSE,
step.sign = FALSE, ...),
')?')

REs <- REs[c("tonalChroma", "colon", "figqual", "bass")]



if (collapse) setNames(cREs(REs), 'harte') else REs

}

makeRE.figuredBass <- function(..., bass.sep = ' ', collapse = TRUE) {
REs <- c(bass = unname(makeRE.kern(..., collapse = TRUE)),
bass.sep = paste0(bass.sep, '?'),
Expand Down
1 change: 0 additions & 1 deletion _pkgdown.yml
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,6 @@ reference:
- key
- signature
- romanKey
- roman
- harm
- chord
- tertian
Expand Down
2 changes: 1 addition & 1 deletion docs/404.html

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion docs/LICENSE-text.html

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions docs/articles/ComplexSyntax.html

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion docs/articles/Context.html

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 96f9c84

Please sign in to comment.