diff --git a/DESCRIPTION b/DESCRIPTION index 51b1b83..0fdbcf3 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: hrep Title: Harmony Representations -Version: 0.8.0 +Version: 0.9.0 Authors@R: person("Peter", "Harrison", email = "pmc.harrison@gmail.com", role = c("aut", "cre")) Description: This package provides methods for representing and manipulating chord sequences. Depends: R (>= 3.4.0) @@ -25,6 +25,7 @@ Imports: tuneR (>= 1.3.3), abcR (>= 0.0.0.9005), tibble (>= 1.4.2), - utils + utils, + glue RdMacros: Rdpack Remotes: pmcharrison/abcR diff --git a/NAMESPACE b/NAMESPACE index 1b071cd..3968a52 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -137,7 +137,7 @@ S3method(pi_chord_type,pi_chord) S3method(pi_chord_type,smooth_spectrum) S3method(pi_chord_type,sparse_spectrum) S3method(pitch,sparse_pi_spectrum) -S3method(play_pluck,default) +S3method(play_sox,default) S3method(plot,smooth_spectrum) S3method(plot,sparse_spectrum) S3method(plot,wave) @@ -162,11 +162,13 @@ S3method(represent,default) S3method(represent,list) S3method(represent,vec) S3method(sample_rate,numeric) -S3method(save_wav_pluck,coded_vec) -S3method(save_wav_pluck,default) -S3method(save_wav_pluck,fr_chord) -S3method(save_wav_pluck,pi_chord) -S3method(save_wav_pluck,vec) +S3method(save_wav_sox,coded_vec) +S3method(save_wav_sox,default) +S3method(save_wav_sox,fr_chord) +S3method(save_wav_sox,pi_chord) +S3method(save_wav_sox,sparse_fr_spectrum) +S3method(save_wav_sox,sparse_pi_spectrum) +S3method(save_wav_sox,vec) S3method(sparse_fr_spectrum,default) S3method(sparse_fr_spectrum,list) S3method(sparse_fr_spectrum,pi_chord) @@ -260,10 +262,10 @@ export(pi_chord) export(pi_chord_type) export(pi_to_pc) export(pitch) -export(play_pluck) +export(play_sox) export(represent) export(sample_rate) -export(save_wav_pluck) +export(save_wav_sox) export(sparse_fr_spectrum) export(sparse_pi_spectrum) export(sum_amplitudes) diff --git a/NEWS.md b/NEWS.md index d7b002f..5c476b5 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,7 @@ +# hrep 0.9.0 + +- Expanding sound synthesis capabilities. + # hrep 0.8.0 - Added methods for synthesising audio. diff --git a/R/play-pluck.R b/R/play-sox.R similarity index 58% rename from R/play-pluck.R rename to R/play-sox.R index 59e84fd..7b6a956 100644 --- a/R/play-pluck.R +++ b/R/play-sox.R @@ -1,8 +1,8 @@ -#' Play sound (pluck) +#' Play sound (sox) #' -#' Plays a sound with a plucked timbre. +#' Plays a sound using the command-line tool sox. #' -#' The sound is synthesised using \code{\link{save_wav_pluck}} +#' The sound is synthesised using \code{\link{save_wav_sox}} #' and saved to a temporary file, which is then played from the R session. #' #' @note @@ -11,18 +11,18 @@ #' must be installed and available on the command line, #' making available the commands \code{sox} and \code{play}. #' -#' @param x Object to play (see \code{\link{save_wav_pluck}} for valid options). -#' @param ... Further parameters to pass to \code{\link{save_wav_pluck}}. +#' @param x Object to play (see \code{\link{save_wav_sox}} for valid options). +#' @param ... Further parameters to pass to \code{\link{save_wav_sox}}. #' #' @export -play_pluck <- function(x, ...) { - UseMethod("play_pluck") +play_sox <- function(x, ...) { + UseMethod("play_sox") } #' @export -play_pluck.default <- function(x, ...) { +play_sox.default <- function(x, ...) { file <- tempfile(fileext = ".wav") - save_wav_pluck(x, file = file, ...) + save_wav_sox(x, file = file, ...) system(paste0("play ", shQuote(file))) file.remove(file) } diff --git a/R/save-wav-pluck.R b/R/save-wav-pluck.R deleted file mode 100644 index e79a968..0000000 --- a/R/save-wav-pluck.R +++ /dev/null @@ -1,60 +0,0 @@ -#' Save wav file (pluck) -#' -#' Saves object to a wav file using the 'pluck' timbre from sox -#' (\url{http://sox.sourceforge.net/}). -#' -#' @note -#' The command-line sound-processing program sox -#' (\url{http://sox.sourceforge.net/}) -#' must be installed and available on the command line -#' under the command \code{sox}. -#' -#' @param x Object to save; methods exist for individual chords -#' and for vectors of chords (see \code{\link{vec}}). -#' Chords are coerced to a \code{\link{pi_chord}} representation. -#' @param file (Character scalar) Output file. -#' @param chord_length (Numeric scalar) Length of each output chord, in seconds. -#' @param ... Parameters passed to methods. -#' -#' @export -save_wav_pluck <- function(x, file, chord_length = 1) { - UseMethod("save_wav_pluck") -} - -#' @export -save_wav_pluck.default <- function(x, ...) { - save_wav_pluck(pi_chord(x), ...) -} - -#' @export -save_wav_pluck.vec <- function(x, file, ...) { - files <- paste("chord-", seq_along(x), "-", sep = "") %>% - tempfile(fileext = ".wav") - for (i in seq_along(x)) save_wav_pluck(x[[i]], files[i], ...) - cmd <- c("sox", shQuote(files), shQuote(file)) %>% paste(collapse = " ") - system(cmd) - file.remove(files) -} - -#' @export -save_wav_pluck.coded_vec <- function(x, file, ...) { - save_wav_pluck(decode(x), file, ...) -} - -#' @export -save_wav_pluck.pi_chord <- function(x, ...) { - save_wav_pluck(fr_chord(x), ...) -} - -#' @export -save_wav_pluck.fr_chord <- function(x, file, chord_length = 1) { - checkmate::qassert(file, "S1") - checkmate::qassert(chord_length, "N1(0,)") - len <- sprintf("%.10f", as.numeric(chord_length)) - freq <- sprintf("%.10f", as.numeric(x)) - tones <- purrr::map_chr(freq, - ~ sprintf('"|sox -n -p synth %s pluck %s"', len, .)) %>% - paste(collapse = " ") - cmd <- sprintf('sox --norm=-3 -m %s %s', tones, file) - system(cmd) -} diff --git a/R/save-wav-sox.R b/R/save-wav-sox.R new file mode 100644 index 0000000..89730cb --- /dev/null +++ b/R/save-wav-sox.R @@ -0,0 +1,141 @@ +#' Save wav file (sox) +#' +#' Saves object to a wav file using sox +#' (\url{http://sox.sourceforge.net/}). +#' +#' @note +#' The command-line sound-processing program sox +#' (\url{http://sox.sourceforge.net/}) +#' must be installed and available on the command line +#' under the command \code{sox}. +#' +#' @param x Object to save; methods exist for individual chords +#' and for vectors of chords (see \code{\link{vec}}). +#' Chords are coerced to a \code{\link{pi_chord}} representation. +#' +#' @param file (Character scalar) Output file. +#' +#' @param ... Parameters passed to methods. +#' +#' @rdname save_wav_sox +#' @export +save_wav_sox <- function(x, file, ...) { + UseMethod("save_wav_sox") +} + +#' @export +save_wav_sox.default <- function(x, file, ...) { + save_wav_sox(pi_chord(x), file, ...) +} + +#' @export +#' @rdname save_wav_sox +save_wav_sox.coded_vec <- function(x, file, ...) { + save_wav_sox(decode(x), file, ...) +} + +#' @export +#' @rdname save_wav_sox +save_wav_sox.pi_chord <- function(x, file, ...) { + save_wav_sox(fr_chord(x), file, ...) +} + +#' @export +#' @rdname save_wav_sox +save_wav_sox.sparse_pi_spectrum <- function(x, file, ...) { + save_wav_sox(sparse_fr_spectrum(x), file, ...) +} + +#' @export +#' @rdname save_wav_sox +#' +#' @param timbre +#' (Character scalar) +#' Timbre to use for synthesizing tones. +#' Only applies to representations that have not already undergone +#' harmonic expansion. +#' This parameter is passed to the \code{type} argument of +#' the \code{sox synth} command. +#' Valid values include +#' sine, square, triangle, sawtooth, trapezium, exp, noise, +#' tpdfnoise, pinknoise, brownnoise, pluck. +save_wav_sox.fr_chord <- function(x, file, timbre = "pluck", ...) { + save_wav_sox( + .sparse_fr_spectrum(frequency = as.numeric(x), + amplitude = rep(1, times = length(x))), + file = file, + timbre = timbre, + ... + ) +} + +#' @export +#' @rdname save_wav_sox +#' +#' @param reverb +#' (Numeric scalar) +#' Reverberance parameter for \code{sox reverb}, +#' expressed as a percentage. +save_wav_sox.vec <- function(x, file, reverb = 20, ...) { + files <- paste("chord-", seq_along(x), "-", sep = "") %>% + tempfile(fileext = ".wav") + for (i in seq_along(x)) save_wav_sox(x[[i]], files[i], ...) + cmd <- c("sox", shQuote(files), shQuote(file), + "reverb", reverb) %>% + paste(collapse = " ") + system(cmd) + file.remove(files) + invisible() +} + +#' @export +#' @rdname save_wav_sox +#' +#' @param chord_length +#' (Numeric scalar) +#' Chord length (seconds). +#' +#' @param rise_length +#' (Numeric scalar) +#' Chord fade-in time (seconds). +#' +#' @param fade_length +#' (Numeric scalar) +#' Chord fade-out time (seconds). +#' +#' @param spectrum_gain +#' (Coerced to character scalar) +#' If "auto", then the chord's audio output is peak-normalised to +#' a fixed value (using "--norm=-3" in sox). +#' Other values will be passed directly to the "gain" parameter in sox. +#' +#' @param volume +#' (Numeric scalar) +#' Amplitude multipler (higher values make the sound louder). +#' Only relevant when \code{spectrum_gain} is not \code{auto}. +#' +save_wav_sox.sparse_fr_spectrum <- function(x, + file, + chord_length = 1, + rise_length = 0.01, + fade_length = 0.01, + timbre = "sine", + spectrum_gain = "auto", + volume = 0.1, + ...) { + stopifnot(length(spectrum_gain) == 1) + fade_length <- fade_length + frequencies <- freq(x) + if (timbre == "pluck" && any(frequencies > 4000)) + stop("cannot play plucked notes with frequencies greater than 4,000 Hz") + amplitudes <- amp(x) * volume + tone_cmd <- purrr::map2_chr(frequencies, amplitudes, function(fr, amp) { + '-v {amp} "|sox -n -p synth {chord_length} {timbre} {fr}"' %>% + glue::glue() + }) %>% paste(collapse = " ") + norm_cmd <- if (spectrum_gain == "auto") "--norm=-3" else "" + gain_cmd <- if (spectrum_gain == "auto") "" else paste0("gain ", spectrum_gain) + "sox {norm_cmd} -m {tone_cmd} {file} {gain_cmd} fade {rise_length} {chord_length} {fade_length}" %>% + glue::glue() %>% + system() +} diff --git a/man/play_pluck.Rd b/man/play_pluck.Rd deleted file mode 100644 index f4b946f..0000000 --- a/man/play_pluck.Rd +++ /dev/null @@ -1,26 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/play-pluck.R -\name{play_pluck} -\alias{play_pluck} -\title{Play sound (pluck)} -\usage{ -play_pluck(x, ...) -} -\arguments{ -\item{x}{Object to play (see \code{\link{save_wav_pluck}} for valid options).} - -\item{...}{Further parameters to pass to \code{\link{save_wav_pluck}}.} -} -\description{ -Plays a sound with a plucked timbre. -} -\details{ -The sound is synthesised using \code{\link{save_wav_pluck}} -and saved to a temporary file, which is then played from the R session. -} -\note{ -The command-line sound-processing program sox -(\url{http://sox.sourceforge.net/}) -must be installed and available on the command line, -making available the commands \code{sox} and \code{play}. -} diff --git a/man/play_sox.Rd b/man/play_sox.Rd new file mode 100644 index 0000000..ab83af7 --- /dev/null +++ b/man/play_sox.Rd @@ -0,0 +1,26 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/play-sox.R +\name{play_sox} +\alias{play_sox} +\title{Play sound (sox)} +\usage{ +play_sox(x, ...) +} +\arguments{ +\item{x}{Object to play (see \code{\link{save_wav_sox}} for valid options).} + +\item{...}{Further parameters to pass to \code{\link{save_wav_sox}}.} +} +\description{ +Plays a sound using the command-line tool sox. +} +\details{ +The sound is synthesised using \code{\link{save_wav_sox}} +and saved to a temporary file, which is then played from the R session. +} +\note{ +The command-line sound-processing program sox +(\url{http://sox.sourceforge.net/}) +must be installed and available on the command line, +making available the commands \code{sox} and \code{play}. +} diff --git a/man/save_wav_pluck.Rd b/man/save_wav_pluck.Rd deleted file mode 100644 index 8cbb7f2..0000000 --- a/man/save_wav_pluck.Rd +++ /dev/null @@ -1,29 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/save-wav-pluck.R -\name{save_wav_pluck} -\alias{save_wav_pluck} -\title{Save wav file (pluck)} -\usage{ -save_wav_pluck(x, file, chord_length = 1) -} -\arguments{ -\item{x}{Object to save; methods exist for individual chords -and for vectors of chords (see \code{\link{vec}}). -Chords are coerced to a \code{\link{pi_chord}} representation.} - -\item{file}{(Character scalar) Output file.} - -\item{chord_length}{(Numeric scalar) Length of each output chord, in seconds.} - -\item{...}{Parameters passed to methods.} -} -\description{ -Saves object to a wav file using the 'pluck' timbre from sox -(\url{http://sox.sourceforge.net/}). -} -\note{ -The command-line sound-processing program sox -(\url{http://sox.sourceforge.net/}) -must be installed and available on the command line -under the command \code{sox}. -} diff --git a/man/save_wav_sox.Rd b/man/save_wav_sox.Rd new file mode 100644 index 0000000..04f2ef4 --- /dev/null +++ b/man/save_wav_sox.Rd @@ -0,0 +1,79 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/save-wav-sox.R +\name{save_wav_sox} +\alias{save_wav_sox} +\alias{save_wav_sox.coded_vec} +\alias{save_wav_sox.pi_chord} +\alias{save_wav_sox.sparse_pi_spectrum} +\alias{save_wav_sox.fr_chord} +\alias{save_wav_sox.vec} +\alias{save_wav_sox.sparse_fr_spectrum} +\title{Save wav file (sox)} +\usage{ +save_wav_sox(x, file, ...) + +\method{save_wav_sox}{coded_vec}(x, file, ...) + +\method{save_wav_sox}{pi_chord}(x, file, ...) + +\method{save_wav_sox}{sparse_pi_spectrum}(x, file, ...) + +\method{save_wav_sox}{fr_chord}(x, file, timbre = "pluck", ...) + +\method{save_wav_sox}{vec}(x, file, reverb = 20, ...) + +\method{save_wav_sox}{sparse_fr_spectrum}(x, file, chord_length = 1, + rise_length = 0.01, fade_length = 0.01, timbre = "sine", + spectrum_gain = "auto", volume = 0.1, ...) +} +\arguments{ +\item{x}{Object to save; methods exist for individual chords +and for vectors of chords (see \code{\link{vec}}). +Chords are coerced to a \code{\link{pi_chord}} representation.} + +\item{file}{(Character scalar) Output file.} + +\item{...}{Parameters passed to methods.} + +\item{timbre}{(Character scalar) +Timbre to use for synthesizing tones. +Only applies to representations that have not already undergone +harmonic expansion. +This parameter is passed to the \code{type} argument of +the \code{sox synth} command. +Valid values include +sine, square, triangle, sawtooth, trapezium, exp, noise, +tpdfnoise, pinknoise, brownnoise, pluck.} + +\item{reverb}{(Numeric scalar) +Reverberance parameter for \code{sox reverb}, +expressed as a percentage.} + +\item{chord_length}{(Numeric scalar) +Chord length (seconds).} + +\item{rise_length}{(Numeric scalar) +Chord fade-in time (seconds).} + +\item{fade_length}{(Numeric scalar) +Chord fade-out time (seconds).} + +\item{spectrum_gain}{(Coerced to character scalar) +If "auto", then the chord's audio output is peak-normalised to +a fixed value (using "--norm=-3" in sox). +Other values will be passed directly to the "gain" parameter in sox.} + +\item{volume}{(Numeric scalar) +Amplitude multipler (higher values make the sound louder). +Only relevant when \code{spectrum_gain} is not \code{auto}.} +} +\description{ +Saves object to a wav file using sox +(\url{http://sox.sourceforge.net/}). +} +\note{ +The command-line sound-processing program sox +(\url{http://sox.sourceforge.net/}) +must be installed and available on the command line +under the command \code{sox}. +}