R/repeats.R

Defines functions volta pct rp

Documented in pct rp volta

#' Repeat phrases
#'
#' Create a repeat section in LilyPond readable format.
#'
#' These functions wraps a phrase object or a character string in LilyPond
#' repeat syntax. The most basic is `rp()` for basic wrapping a LilyPond
#' `unfold` repeat tag around a phrase. This repeats the phrase `n` times, but
#' it is displayed in the engraved sheet music fully written out as a literal
#' propagation of the phrase with no repeat notation used to reduce redundant
#' presentation. The next is `pct()`, which wraps a `percent()` repeat tag
#' around a phrase. This is displayed in sheet music as percent repeat notation
#' whose specific notation changes based on the length of the repeated section
#' of music, used for beats or whole measures. `volta()` wraps a phrase in a
#' `volta()` repeat tag, used for long repeats of one or more full measures or
#' bars of music, optionally with alternate endings.
#'
#' Note that basic strings should still be interpretable as a valid musical
#' phrase by LilyPond and such strings will be coerced to the phrase class by
#' these functions. For example, a one-measure rest, `"r1"`, does not need to be
#' a phrase object to work with these functions, nor does any other character
#' string explicitly written out in valid LilyPond syntax. As always, see the
#' LilyPond documentation if you are not familiar with LilyPond syntax.
#'
#' VOLTA REPEAT: When `silent = TRUE` there is no indication of the number
#' of plays above the staff at the start of the volta section. This otherwise
#' happens automatically when the number of repeats is greater than one and no
#' alternate endings are included (which are already numbered).
#' This override creates ambiguity on its own, but is important to use multiple
#' staves are present and another staff already displays the text regarding the
#' number or plays. This prevents printing the same text above every staff.
#'
#' PERCENT REPEAT: As indicated in the parameter descriptions, the arguments
#' `counter` and `step` only apply to full measures or bars of music.
#' It does not apply to shorter beats that are repeated using `pct()`.
#'
#' @param phrase a phrase object or equivalent string to be repeated.
#' @param n integer, number of repeats of `phrase` (one less than the total
#' number of plays).
#' @param endings a single phrase or a list of phrases, alternate endings.
#' @param silent if `TRUE`, no text will be printed above the staff at the
#' beginning of a volta section. See details.
#' @param counter logical, if `TRUE`, print the percent repeat counter above the
#' staff, applies only to *measure* repeats of more than two repeats (`n` > 2).
#' @param step integer, print the *measure* percent repeat counter above the
#' staff only at every `step` measures when `counter = TRUE`.
#' @param reset logical, percent repeat `counter` and `step` settings are only
#' applied to the single `pct()` call and are reset afterward. If
#' `reset = FALSE`, the settings are left open to apply to any subsequent
#' percent repeat sections in a track.
#'
#' @return a phrase.
#' @name repeats
#' @seealso [phrase()]
#' @export
#'
#' @examples
#' x <- phrase("c ec'g' ec'g'", "4 4 2", "5 432 432")
#' e1 <- phrase("a", 1, 5) # ending 1
#' e2 <- phrase("b", 1, 5) # ending 2
#'
#' rp(x) # simple unfolded repeat, one repeat or two plays
#' rp(x, 3) # three repeats or four plays
#'
#' pct(x) # one repeat or two plays
#' pct(x, 9, TRUE, 5) # 10 plays, add counter every 5 steps
#' pct(x, 9, TRUE, 5, FALSE) # as above, but do not reset counter settings
#'
#' volta(x) # one repeat or two plays
#' volta(x, 1, list(e1, e2)) # one repeat with alternate ending
#' volta(x, 4, list(e1, e2)) # multiple repeats with only one alternate ending
#' volta(x, 4) # no alternates, more than one repeat

#' @export
#' @rdname repeats
rp <- function(phrase, n = 1){
  x <- paste("\\repeat unfold", n + 1, "{", paste(phrase, collapse = " "),
             "}\n")
  as_phrase(x)
}

#' @export
#' @rdname repeats
pct <- function(phrase, n = 1, counter = FALSE, step = 1, reset = TRUE){
  x <- paste("\\repeat percent", n + 1, "{", paste(phrase, collapse = " "),
             "}\n")
  if(counter) x <- paste0(
    "\\set countPercentRepeats = ##",
    ifelse(counter, "t", "f"),
    "\n\\set repeatCountVisibility = #(every-nth-repeat-count-visible ",
    step, ")\n", x)
  if(counter & reset) x <- paste0(
    x, "\\set countPercentRepeats = ##f\n",
    "\\set repeatCountVisibility = #(every-nth-repeat-count-visible 1)\n")
  as_phrase(x)
}

#' @export
#' @rdname repeats
volta <- function(phrase, n = 1, endings = NULL, silent = FALSE){
  if(n > 1 & is.null(endings) & !silent){
    phrase <- strsplit(phrase, " ")[[1]]
    idx <- grep(">", phrase)
    if(length(idx)){
      idx <- min(idx)
      phrase[idx] <- paste0(phrase[idx], paste("^\"Play", n + 1, "times.\""))
    }
    phrase <- paste0(phrase, collapse = " ")
  }
  x <- paste("\\repeat volta", n + 1, "{", paste(phrase, collapse = " "),
             "| }\n")
  if(!is.null(endings)){
    if(!is.list(endings) & length(endings) > 1)
      stop("`endings` must be a list of phrases or a single phrase.",
           call. = FALSE)
    if(length(endings) == 1) endings <- as.list(endings)
    x <- paste0(x, "\\alternative {\n",
                paste("  {", unlist(endings), "| }\n", collapse = ""), "}")
  }
  x <- gsub("\\| \\|", "\\|", x)
  as_phrase(x)
}
leonawicz/tabr documentation built on Sept. 24, 2023, 2:49 p.m.