Skip to content

Commit

Permalink
Merge pull request #10 from pmcharrison/sox
Browse files Browse the repository at this point in the history
Expanding sound synthesis capabilities
  • Loading branch information
pmcharrison committed Apr 6, 2019
2 parents 766f696 + 982b65f commit f70ae76
Show file tree
Hide file tree
Showing 10 changed files with 272 additions and 134 deletions.
5 changes: 3 additions & 2 deletions 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)
Expand All @@ -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
18 changes: 10 additions & 8 deletions NAMESPACE
Expand Up @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
4 changes: 4 additions & 0 deletions NEWS.md
@@ -1,3 +1,7 @@
# hrep 0.9.0

- Expanding sound synthesis capabilities.

# hrep 0.8.0

- Added methods for synthesising audio.
Expand Down
18 changes: 9 additions & 9 deletions R/play-pluck.R → 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
Expand All @@ -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)
}
60 changes: 0 additions & 60 deletions R/save-wav-pluck.R

This file was deleted.

141 changes: 141 additions & 0 deletions 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()
}
26 changes: 0 additions & 26 deletions man/play_pluck.Rd

This file was deleted.

26 changes: 26 additions & 0 deletions man/play_sox.Rd

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

29 changes: 0 additions & 29 deletions man/save_wav_pluck.Rd

This file was deleted.

0 comments on commit f70ae76

Please sign in to comment.