From d09197bbc4fc7522403f5ee09f113796507f07fa Mon Sep 17 00:00:00 2001 From: Peter Harrison Date: Fri, 14 May 2021 09:52:28 +0200 Subject: [PATCH 1/2] Add and propagate a 'coherent' option for summing amplitudes --- NEWS.md | 6 +++++ R/expand-harmonics.R | 20 +++++++++----- R/smooth-pc-spectrum.R | 34 +++++++++++++++++------- R/smooth-pi-spectrum.R | 10 ++++--- R/sparse-pc-spectrum.R | 13 +++++---- R/sparse-pi-spectrum.R | 4 +++ R/sparse-spectrum.R | 32 ++++++++++++++++++---- man/collapse_summing_amplitudes.Rd | 28 +++++++++++++++++++ man/combine_sparse_spectra.Rd | 8 +++++- man/expand_harmonics.Rd | 18 ++++++++++--- man/play_wav.Rd | 5 ++++ man/save_wav.Rd | 5 ++++ man/smooth_pc_spectrum.Rd | 30 +++++++++++++++++---- man/smooth_pi_spectrum.Rd | 17 ++++++++++-- man/sparse_fr_spectrum.Rd | 5 ++++ man/sparse_pc_spectrum.Rd | 10 +++++-- man/sparse_pi_spectrum.Rd | 8 +++++- man/wave.Rd | 5 ++++ tests/testthat/test-sparse_pc_spectrum.R | 15 +++++++++++ 19 files changed, 228 insertions(+), 45 deletions(-) create mode 100644 man/collapse_summing_amplitudes.Rd diff --git a/NEWS.md b/NEWS.md index 99bbba0..5606818 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,9 @@ +- Add and propagate a `coherent` option for summing amplitudes. +Setting `coherent == TRUE` means coherent phases, so amplitudes are summed using addition; +setting `coherent == FALSE` means incoherent phases, so amplitudes are summed using +the root mean square. This argument is now available in many `hrep` functions +where spectra are constructed. + # hrep 0.14.0 - Add `[.wave` method. diff --git a/R/expand-harmonics.R b/R/expand-harmonics.R index 1e35db7..57b2bde 100644 --- a/R/expand-harmonics.R +++ b/R/expand-harmonics.R @@ -23,12 +23,15 @@ #' If TRUE, then the harmonics in the resulting spectrum are labelled with their harmonic numbers. #' #' @rdname expand_harmonics +#' +#' @inheritParams collapse_summing_amplitudes #' @export expand_harmonics <- function(x, num_harmonics = 11L, roll_off = 1, digits = 6, - label_harmonics = FALSE) { + label_harmonics = FALSE, + coherent = FALSE) { UseMethod("expand_harmonics") } @@ -38,12 +41,14 @@ expand_harmonics.sparse_fr_spectrum <- function(x, num_harmonics = 11L, roll_off = 1, digits = 6, - label_harmonics = FALSE) { + label_harmonics = FALSE, + coherent = FALSE) { expand_harmonics(sparse_pi_spectrum(x), num_harmonics = num_harmonics, roll_off = roll_off, digits = digits, - label_harmonics = label_harmonics) %>% + label_harmonics = label_harmonics, + coherent = coherent) %>% sparse_fr_spectrum() } @@ -53,9 +58,9 @@ expand_harmonics.sparse_pi_spectrum <- function(x, num_harmonics = 11L, roll_off = 1, digits = 6, - label_harmonics = FALSE) { + label_harmonics = FALSE, + coherent = FALSE) { template <- pi_harmonic_template(num_harmonics, roll_off) - purrr::map2(pitch(x), amp(x), function(pitch, amp) { df <- data.frame( @@ -65,7 +70,7 @@ expand_harmonics.sparse_pi_spectrum <- function(x, if (label_harmonics) df$labels <- seq_along(template$interval) df }) %>% - collapse_summing_amplitudes(digits = digits) %>% + collapse_summing_amplitudes(digits = digits, coherent = coherent) %>% {.sparse_pi_spectrum(pitch = .$x, amplitude = .$y, labels = .$labels)} } @@ -75,7 +80,8 @@ expand_harmonics.pi_chord <- function(x, num_harmonics = 11L, roll_off = 1, digits = 6, - label_harmonics = FALSE) { + label_harmonics = FALSE, + coherent = FALSE) { sparse_pi_spectrum(x, num_harmonics = num_harmonics, roll_off = roll_off, diff --git a/R/smooth-pc-spectrum.R b/R/smooth-pc-spectrum.R index 0c14dad..6e90dfe 100644 --- a/R/smooth-pc-spectrum.R +++ b/R/smooth-pc-spectrum.R @@ -56,6 +56,7 @@ is.smooth_pc_spectrum <- function(x) { #' Provided for S3 method consistency. #' #' @inheritParams expand_harmonics +#' @inheritParams collapse_summing_amplitudes #' #' @seealso #' This representation was inspired by \code{\link{milne_pc_spectrum}}, @@ -69,27 +70,40 @@ is.smooth_pc_spectrum <- function(x) { #' \insertAllCited{} #' #' @export -smooth_pc_spectrum <- function(x, sigma = 6.83, ...) { +smooth_pc_spectrum <- function( + x, + ..., + sigma = 6.83, + num_harmonics = 11L, + roll_off = 1, + coherent = FALSE +) { UseMethod("smooth_pc_spectrum") } #' @rdname smooth_pc_spectrum #' @export -smooth_pc_spectrum.default <- function(x, - sigma = 6.83, - num_harmonics = 11L, - roll_off = 1, - ...) { +smooth_pc_spectrum.default <- function( + x, + ..., + sigma = 6.83, + num_harmonics = 11L, + roll_off = 1, + coherent = FALSE +) { smooth_pc_spectrum(sparse_pc_spectrum(x, num_harmonics = num_harmonics, - roll_off = roll_off), - sigma = sigma, ...) + roll_off = roll_off, + coherent = coherent), + sigma = sigma, + coherent = coherent, + ...) } #' @rdname smooth_pc_spectrum #' @export -smooth_pc_spectrum.sparse_pc_spectrum <- function(x, sigma = 6.83, ...) { - df <- collapse_summing_amplitudes(list(x), digits = 2, modulo = 12) +smooth_pc_spectrum.sparse_pc_spectrum <- function(x, ..., sigma = 6.83, coherent = FALSE) { + df <- collapse_summing_amplitudes(list(x), digits = 2, modulo = 12, coherent = coherent) df$ind <- 1 + df$x * 100 checkmate::qassert(df$ind, "X[1,12000]") diff --git a/R/smooth-pi-spectrum.R b/R/smooth-pi-spectrum.R index fdaa6a4..8133cbc 100644 --- a/R/smooth-pi-spectrum.R +++ b/R/smooth-pi-spectrum.R @@ -56,6 +56,7 @@ is.smooth_pi_spectrum <- function(x) { #' Provided for S3 method consistency. #' #' @inheritParams expand_harmonics +#' @inheritParams collapse_summing_amplitudes #' #' @seealso #' This representation was inspired by \code{\link{milne_pc_spectrum}}, @@ -79,18 +80,21 @@ smooth_pi_spectrum.default <- function(x, sigma = 6.83, num_harmonics = 11L, roll_off = 1, + coherent = FALSE, ...) { smooth_pi_spectrum(sparse_pi_spectrum(x, num_harmonics = num_harmonics, roll_off = roll_off, + coherent = coherent, ...), - sigma = sigma) + sigma = sigma, + coherent = coherent) } #' @rdname smooth_pi_spectrum #' @export -smooth_pi_spectrum.sparse_pi_spectrum <- function(x, sigma = 6.83, ...) { - df <- collapse_summing_amplitudes(list(x), digits = 2) +smooth_pi_spectrum.sparse_pi_spectrum <- function(x, sigma = 6.83, coherent = FALSE, ...) { + df <- collapse_summing_amplitudes(list(x), digits = 2, coherent = coherent) df$ind <- 1 + df$x * 100 checkmate::qassert(df$ind, "X[1,12000]") diff --git a/R/sparse-pc-spectrum.R b/R/sparse-pc-spectrum.R index b2fed3d..a4fe69f 100644 --- a/R/sparse-pc-spectrum.R +++ b/R/sparse-pc-spectrum.R @@ -56,6 +56,7 @@ is.sparse_pc_spectrum <- function(x) { #' #' @param x Input sonority. #' +#' @inheritParams collapse_summing_amplitudes #' @inheritDotParams expand_harmonics #' #' @return An object of class \code{sparse_pc_spectrum}. @@ -71,13 +72,13 @@ sparse_pc_spectrum.sparse_pc_spectrum <- function(x, ...) { x } -sparse_pc_spectrum.sparse_pi_spectrum <- function(x, digits = 6) { +sparse_pc_spectrum.sparse_pi_spectrum <- function(x, digits = 6, coherent = FALSE, ...) { df <- data.frame(x = pitch(x), y = amp(x)) if (!is.null(x$labels)) df$labels <- x$labels df %>% list() %>% - collapse_summing_amplitudes(digits = digits, modulo = 12) %>% + collapse_summing_amplitudes(digits = digits, modulo = 12, coherent = coherent) %>% { .sparse_pc_spectrum(pc = .[[1]], amplitude = .[[2]], @@ -87,8 +88,8 @@ sparse_pc_spectrum.sparse_pi_spectrum <- function(x, digits = 6) { #' @rdname sparse_pc_spectrum #' @export -sparse_pc_spectrum.sparse_fr_spectrum <- function(x, ...) { - sparse_pc_spectrum(sparse_pi_spectrum(x)) +sparse_pc_spectrum.sparse_fr_spectrum <- function(x, coherent = FALSE, ...) { + sparse_pc_spectrum(sparse_pi_spectrum(x, coherent = coherent), coherent = coherent, ...) } #' @rdname sparse_pc_spectrum @@ -117,8 +118,10 @@ sparse_pc_spectrum.default <- function(x, ...) { #' @export sparse_pc_spectrum.pi_chord <- function(x, amplitude = 1, + coherent = FALSE, ...) { - sparse_pc_spectrum(sparse_pi_spectrum(x, amplitude = amplitude, ...)) + sparse_pc_spectrum(sparse_pi_spectrum(x, amplitude = amplitude, coherent = coherent, ...), + coherent = coherent) } #' @export diff --git a/R/sparse-pi-spectrum.R b/R/sparse-pi-spectrum.R index 7b2ad3b..542b43f 100644 --- a/R/sparse-pi-spectrum.R +++ b/R/sparse-pi-spectrum.R @@ -106,15 +106,19 @@ sparse_pi_spectrum.default <- function(x, ...) { #' #' @rdname sparse_pi_spectrum #' +#' @inheritParams expand_harmonics +#' #' @export sparse_pi_spectrum.pi_chord <- function(x, amplitude = 1, + coherent = FALSE, ...) { checkmate::qassert(amplitude, "N") if (length(amplitude) == 1L) amplitude <- rep_to_match(amplitude, x) stopifnot(length(amplitude) == length(x)) expand_harmonics(.sparse_pi_spectrum(pitch = as.numeric(x), amplitude = amplitude), + coherent = coherent, ...) } diff --git a/R/sparse-spectrum.R b/R/sparse-spectrum.R index 4cbba3e..253934f 100644 --- a/R/sparse-spectrum.R +++ b/R/sparse-spectrum.R @@ -168,8 +168,10 @@ transform_y.sparse_spectrum <- function(x, f, y_unit, y_lab) { #' #' @return A sparse spectrum object. #' +#' @inheritParams collapse_summing_amplitudes +#' #' @export -combine_sparse_spectra <- function(..., digits = 6) { +combine_sparse_spectra <- function(..., digits = 6, coherent = FALSE) { checkmate::qassert(digits, "X1[0,)") input <- list(...) if (length(input) == 0) stop("combine_sparse_spectra needs at least 1 input") @@ -199,7 +201,7 @@ combine_sparse_spectra <- function(..., digits = 6) { res <- lapply(input, as.data.frame) %>% - collapse_summing_amplitudes(digits = digits) %>% + collapse_summing_amplitudes(digits = digits, coherent = coherent) %>% { f <- if (octave_invariant) .sparse_pc_spectrum else .sparse_pi_spectrum f(.$x, .$y, labels = .$labels) @@ -208,7 +210,25 @@ combine_sparse_spectra <- function(..., digits = 6) { if (output_class == "sparse_fr_spectrum") sparse_fr_spectrum(res) else res } -collapse_summing_amplitudes <- function(x, digits, modulo = NA_real_) { +#' Collapse summing amplitudes +#' +#' Takes a dataframe of spectral components (locations \code{x}, amplitudes \code{y}), +#' rounds \code{x}, and then combines spectral components with the same location. +#' +#' @param x Input dataframe. +#' +#' @param digits Number of digits to which \code{x} should be rounded. +#' +#' @param modulo Optional modulo value for the rounding of \code{x}. +#' +#' @param coherent Whether the amplitudes from different spectral components should be combined +#' assuming coherent summation, where the amplitudes simply add together +#' (default is \code{FALSE}). +#' Otherwise incoherent summation is used, where the amplitudes are squared, added, then +#' square rooted. +#' +#' @return A dataframe. +collapse_summing_amplitudes <- function(x, digits, modulo = NA_real_, coherent = FALSE) { checkmate::qassert(modulo, "n1(0,)") if (!is.list(x) || !all(purrr::map_lgl(x, ~ is.data.frame(.) && @@ -219,8 +239,10 @@ collapse_summing_amplitudes <- function(x, digits, modulo = NA_real_) { x %>% data.table::rbindlist() %>% { + if (!is.na(modulo)) .$x <- .$x %% modulo .$x <- round(.$x, digits = digits) if (!is.na(modulo)) .$x <- .$x %% modulo + # Modulo needs to be done before and after because of subtle edge cases! . } %>% {reduce_by_key( @@ -228,10 +250,10 @@ collapse_summing_amplitudes <- function(x, digits, modulo = NA_real_) { values = if (has_labels) purrr::map2(.$y, .$labels, ~ list(amplitude = .x, label = .y)) else .$y, function(x, y) { if (has_labels) { - list(amplitude = sum_amplitudes(x, y$amplitude, coherent = FALSE), + list(amplitude = sum_amplitudes(x, y$amplitude, coherent = coherent), label = y$label) } else { - sum_amplitudes(x, y, coherent = FALSE) + sum_amplitudes(x, y, coherent = coherent) } }, key_type = "numeric" diff --git a/man/collapse_summing_amplitudes.Rd b/man/collapse_summing_amplitudes.Rd new file mode 100644 index 0000000..523530a --- /dev/null +++ b/man/collapse_summing_amplitudes.Rd @@ -0,0 +1,28 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/sparse-spectrum.R +\name{collapse_summing_amplitudes} +\alias{collapse_summing_amplitudes} +\title{Collapse summing amplitudes} +\usage{ +collapse_summing_amplitudes(x, digits, modulo = NA_real_, coherent = FALSE) +} +\arguments{ +\item{x}{Input dataframe.} + +\item{digits}{Number of digits to which \code{x} should be rounded.} + +\item{modulo}{Optional modulo value for the rounding of \code{x}.} + +\item{coherent}{Whether the amplitudes from different spectral components should be combined +assuming coherent summation, where the amplitudes simply add together +(default is \code{FALSE}). +Otherwise incoherent summation is used, where the amplitudes are squared, added, then +square rooted.} +} +\value{ +A dataframe. +} +\description{ +Takes a dataframe of spectral components (locations \code{x}, amplitudes \code{y}), +rounds \code{x}, and then combines spectral components with the same location. +} diff --git a/man/combine_sparse_spectra.Rd b/man/combine_sparse_spectra.Rd index c2b7a82..1d4d7fd 100644 --- a/man/combine_sparse_spectra.Rd +++ b/man/combine_sparse_spectra.Rd @@ -4,7 +4,7 @@ \alias{combine_sparse_spectra} \title{Combine sparse spectra} \usage{ -combine_sparse_spectra(..., digits = 6) +combine_sparse_spectra(..., digits = 6, coherent = FALSE) } \arguments{ \item{...}{Sparse spectra to combine @@ -13,6 +13,12 @@ combine_sparse_spectra(..., digits = 6) \item{digits}{(Integerish scalar) The MIDI pitch(-class) of each partial will be rounded to this number of digits.} + +\item{coherent}{Whether the amplitudes from different spectral components should be combined +assuming coherent summation, where the amplitudes simply add together +(default is \code{FALSE}). +Otherwise incoherent summation is used, where the amplitudes are squared, added, then +square rooted.} } \value{ A sparse spectrum object. diff --git a/man/expand_harmonics.Rd b/man/expand_harmonics.Rd index 1000cd9..11aa277 100644 --- a/man/expand_harmonics.Rd +++ b/man/expand_harmonics.Rd @@ -12,7 +12,8 @@ expand_harmonics( num_harmonics = 11L, roll_off = 1, digits = 6, - label_harmonics = FALSE + label_harmonics = FALSE, + coherent = FALSE ) \method{expand_harmonics}{sparse_fr_spectrum}( @@ -20,7 +21,8 @@ expand_harmonics( num_harmonics = 11L, roll_off = 1, digits = 6, - label_harmonics = FALSE + label_harmonics = FALSE, + coherent = FALSE ) \method{expand_harmonics}{sparse_pi_spectrum}( @@ -28,7 +30,8 @@ expand_harmonics( num_harmonics = 11L, roll_off = 1, digits = 6, - label_harmonics = FALSE + label_harmonics = FALSE, + coherent = FALSE ) \method{expand_harmonics}{pi_chord}( @@ -36,7 +39,8 @@ expand_harmonics( num_harmonics = 11L, roll_off = 1, digits = 6, - label_harmonics = FALSE + label_harmonics = FALSE, + coherent = FALSE ) } \arguments{ @@ -56,6 +60,12 @@ in the harmonics, with greater values corresponding to higher roll-off.} \item{digits}{Number of digits to which each partial's MIDI pitch should be rounded.} \item{label_harmonics}{If TRUE, then the harmonics in the resulting spectrum are labelled with their harmonic numbers.} + +\item{coherent}{Whether the amplitudes from different spectral components should be combined +assuming coherent summation, where the amplitudes simply add together +(default is \code{FALSE}). +Otherwise incoherent summation is used, where the amplitudes are squared, added, then +square rooted.} } \description{ Expands each tone in an object into its implied harmonics. diff --git a/man/play_wav.Rd b/man/play_wav.Rd index f64296f..08d65e7 100644 --- a/man/play_wav.Rd +++ b/man/play_wav.Rd @@ -37,6 +37,11 @@ each tone should be expanded.} in the harmonics, with greater values corresponding to higher roll-off.} \item{\code{digits}}{Number of digits to which each partial's MIDI pitch should be rounded.} \item{\code{label_harmonics}}{If TRUE, then the harmonics in the resulting spectrum are labelled with their harmonic numbers.} + \item{\code{coherent}}{Whether the amplitudes from different spectral components should be combined +assuming coherent summation, where the amplitudes simply add together +(default is \code{FALSE}). +Otherwise incoherent summation is used, where the amplitudes are squared, added, then +square rooted.} }} } \description{ diff --git a/man/save_wav.Rd b/man/save_wav.Rd index ab85f94..a990c0e 100644 --- a/man/save_wav.Rd +++ b/man/save_wav.Rd @@ -51,6 +51,11 @@ each tone should be expanded.} in the harmonics, with greater values corresponding to higher roll-off.} \item{\code{digits}}{Number of digits to which each partial's MIDI pitch should be rounded.} \item{\code{label_harmonics}}{If TRUE, then the harmonics in the resulting spectrum are labelled with their harmonic numbers.} + \item{\code{coherent}}{Whether the amplitudes from different spectral components should be combined +assuming coherent summation, where the amplitudes simply add together +(default is \code{FALSE}). +Otherwise incoherent summation is used, where the amplitudes are squared, added, then +square rooted.} }} } \description{ diff --git a/man/smooth_pc_spectrum.Rd b/man/smooth_pc_spectrum.Rd index 5353a1d..570d246 100644 --- a/man/smooth_pc_spectrum.Rd +++ b/man/smooth_pc_spectrum.Rd @@ -6,29 +6,49 @@ \alias{smooth_pc_spectrum.sparse_pc_spectrum} \title{Smooth pitch-class spectrum} \usage{ -smooth_pc_spectrum(x, sigma = 6.83, ...) +smooth_pc_spectrum( + x, + ..., + sigma = 6.83, + num_harmonics = 11L, + roll_off = 1, + coherent = FALSE +) -\method{smooth_pc_spectrum}{default}(x, sigma = 6.83, num_harmonics = 11L, roll_off = 1, ...) +\method{smooth_pc_spectrum}{default}( + x, + ..., + sigma = 6.83, + num_harmonics = 11L, + roll_off = 1, + coherent = FALSE +) -\method{smooth_pc_spectrum}{sparse_pc_spectrum}(x, sigma = 6.83, ...) +\method{smooth_pc_spectrum}{sparse_pc_spectrum}(x, ..., sigma = 6.83, coherent = FALSE) } \arguments{ \item{x}{Object to convert. By default \code{\link{sparse_pc_spectrum}} is called first to convert the object to a sparse pitch-class spectrum.} +\item{...}{Provided for S3 method consistency.} + \item{sigma}{(Numeric scalar) Standard deviation of the Gaussian distribution used to simulate perceptual blurring. Defaults to 6.83 cents, after \insertCite{Milne2016;textual}{hrep}.} -\item{...}{Provided for S3 method consistency.} - \item{num_harmonics}{(Integerish scalar) Number of harmonics (including the fundamental) to which each tone should be expanded.} \item{roll_off}{(Numeric scalar) Parametrises the amount of amplitude roll-off in the harmonics, with greater values corresponding to higher roll-off.} + +\item{coherent}{Whether the amplitudes from different spectral components should be combined +assuming coherent summation, where the amplitudes simply add together +(default is \code{FALSE}). +Otherwise incoherent summation is used, where the amplitudes are squared, added, then +square rooted.} } \description{ Creates an object of class \code{smooth_pc_spectrum}, diff --git a/man/smooth_pi_spectrum.Rd b/man/smooth_pi_spectrum.Rd index e10bf33..2cb8295 100644 --- a/man/smooth_pi_spectrum.Rd +++ b/man/smooth_pi_spectrum.Rd @@ -8,9 +8,16 @@ \usage{ smooth_pi_spectrum(x, sigma = 6.83, ...) -\method{smooth_pi_spectrum}{default}(x, sigma = 6.83, num_harmonics = 11L, roll_off = 1, ...) +\method{smooth_pi_spectrum}{default}( + x, + sigma = 6.83, + num_harmonics = 11L, + roll_off = 1, + coherent = FALSE, + ... +) -\method{smooth_pi_spectrum}{sparse_pi_spectrum}(x, sigma = 6.83, ...) +\method{smooth_pi_spectrum}{sparse_pi_spectrum}(x, sigma = 6.83, coherent = FALSE, ...) } \arguments{ \item{x}{Object to convert. By default \code{\link{sparse_pi_spectrum}} @@ -29,6 +36,12 @@ each tone should be expanded.} \item{roll_off}{(Numeric scalar) Parametrises the amount of amplitude roll-off in the harmonics, with greater values corresponding to higher roll-off.} + +\item{coherent}{Whether the amplitudes from different spectral components should be combined +assuming coherent summation, where the amplitudes simply add together +(default is \code{FALSE}). +Otherwise incoherent summation is used, where the amplitudes are squared, added, then +square rooted.} } \description{ Creates an object of class \code{smooth_pi_spectrum}, diff --git a/man/sparse_fr_spectrum.Rd b/man/sparse_fr_spectrum.Rd index 74d2021..a9732bd 100644 --- a/man/sparse_fr_spectrum.Rd +++ b/man/sparse_fr_spectrum.Rd @@ -43,6 +43,11 @@ each tone should be expanded.} in the harmonics, with greater values corresponding to higher roll-off.} \item{\code{digits}}{Number of digits to which each partial's MIDI pitch should be rounded.} \item{\code{label_harmonics}}{If TRUE, then the harmonics in the resulting spectrum are labelled with their harmonic numbers.} + \item{\code{coherent}}{Whether the amplitudes from different spectral components should be combined +assuming coherent summation, where the amplitudes simply add together +(default is \code{FALSE}). +Otherwise incoherent summation is used, where the amplitudes are squared, added, then +square rooted.} }} } \value{ diff --git a/man/sparse_pc_spectrum.Rd b/man/sparse_pc_spectrum.Rd index ec39b7d..3d6b13c 100644 --- a/man/sparse_pc_spectrum.Rd +++ b/man/sparse_pc_spectrum.Rd @@ -10,13 +10,13 @@ \usage{ sparse_pc_spectrum(x, ...) -\method{sparse_pc_spectrum}{sparse_fr_spectrum}(x, ...) +\method{sparse_pc_spectrum}{sparse_fr_spectrum}(x, coherent = FALSE, ...) \method{sparse_pc_spectrum}{list}(x, ...) \method{sparse_pc_spectrum}{default}(x, ...) -\method{sparse_pc_spectrum}{pi_chord}(x, amplitude = 1, ...) +\method{sparse_pc_spectrum}{pi_chord}(x, amplitude = 1, coherent = FALSE, ...) } \arguments{ \item{x}{Input sonority.} @@ -33,6 +33,12 @@ in the harmonics, with greater values corresponding to higher roll-off.} \item{\code{label_harmonics}}{If TRUE, then the harmonics in the resulting spectrum are labelled with their harmonic numbers.} }} +\item{coherent}{Whether the amplitudes from different spectral components should be combined +assuming coherent summation, where the amplitudes simply add together +(default is \code{FALSE}). +Otherwise incoherent summation is used, where the amplitudes are squared, added, then +square rooted.} + \item{amplitude}{(Numeric vector) Vector of amplitudes to assign to each pitch. If a scalar value is provided, this value is assigned to all pitches} diff --git a/man/sparse_pi_spectrum.Rd b/man/sparse_pi_spectrum.Rd index 764a2cc..0c230bf 100644 --- a/man/sparse_pi_spectrum.Rd +++ b/man/sparse_pi_spectrum.Rd @@ -19,7 +19,7 @@ sparse_pi_spectrum(x, ...) \method{sparse_pi_spectrum}{default}(x, ...) -\method{sparse_pi_spectrum}{pi_chord}(x, amplitude = 1, ...) +\method{sparse_pi_spectrum}{pi_chord}(x, amplitude = 1, coherent = FALSE, ...) } \arguments{ \item{x}{Input sonority.} @@ -39,6 +39,12 @@ in the harmonics, with greater values corresponding to higher roll-off.} \item{amplitude}{(Numeric vector) Vector of amplitudes to assign to each pitch. If a scalar value is provided, this value is assigned to all pitches} + +\item{coherent}{Whether the amplitudes from different spectral components should be combined +assuming coherent summation, where the amplitudes simply add together +(default is \code{FALSE}). +Otherwise incoherent summation is used, where the amplitudes are squared, added, then +square rooted.} } \value{ An object of class \code{sparse_pi_spectrum}. diff --git a/man/wave.Rd b/man/wave.Rd index e2fbfaf..0cf9c4c 100644 --- a/man/wave.Rd +++ b/man/wave.Rd @@ -40,6 +40,11 @@ each tone should be expanded.} in the harmonics, with greater values corresponding to higher roll-off.} \item{\code{digits}}{Number of digits to which each partial's MIDI pitch should be rounded.} \item{\code{label_harmonics}}{If TRUE, then the harmonics in the resulting spectrum are labelled with their harmonic numbers.} + \item{\code{coherent}}{Whether the amplitudes from different spectral components should be combined +assuming coherent summation, where the amplitudes simply add together +(default is \code{FALSE}). +Otherwise incoherent summation is used, where the amplitudes are squared, added, then +square rooted.} }} \item{length_sec}{(Numeric scalar) Length of the output wave, in seconds.} diff --git a/tests/testthat/test-sparse_pc_spectrum.R b/tests/testthat/test-sparse_pc_spectrum.R index 44e8086..da4e503 100644 --- a/tests/testthat/test-sparse_pc_spectrum.R +++ b/tests/testthat/test-sparse_pc_spectrum.R @@ -23,3 +23,18 @@ test_that("misc", { sparse_pi_spectrum(c(60, 64, 67)) %>% sparse_fr_spectrum() %>% sparse_pc_spectrum() ) }) + +test_that("coherent", { + expect_equal( + sparse_pc_spectrum(pi_chord(c(60, 60.000001)), coherent = TRUE, num_harmonics = 2) %>% as.data.frame() %>% `$`(y), + c(1.5, 1.5) # the partials are still separated + ) + expect_equal( + sparse_pc_spectrum(pi_chord(c(60, 60.0000001)), coherent = TRUE, num_harmonics = 2) %>% as.data.frame() %>% `$`(y), + c(3) # we are now within the rounding threshold, the partials now add coherently + ) + expect_equal( + sparse_pc_spectrum(pi_chord(c(60, 60.0000001)), coherent = FALSE, num_harmonics = 1) %>% as.data.frame() %>% `$`(y), + sqrt(2) # testing incoherent addition, this time with just one harmonic + ) +}) From b704604efe2f9d86c2e6aec39a316172bf61672c Mon Sep 17 00:00:00 2001 From: Peter Harrison Date: Fri, 14 May 2021 09:56:30 +0200 Subject: [PATCH 2/2] Increment version number --- DESCRIPTION | 2 +- NEWS.md | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 4cb2198..c5c3fc6 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: hrep Title: Harmony Representations -Version: 0.14.0 +Version: 0.15.0 Authors@R: person("Peter", "Harrison", email = "pmc.harrison@gmail.com", role = c("aut", "cre")) Description: This package provides utilities for representing and manipulating chord sequences for perceptually informed harmony modelling. diff --git a/NEWS.md b/NEWS.md index 5606818..dfb971b 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,5 @@ +# hrep 0.15.0 + - Add and propagate a `coherent` option for summing amplitudes. Setting `coherent == TRUE` means coherent phases, so amplitudes are summed using addition; setting `coherent == FALSE` means incoherent phases, so amplitudes are summed using