Skip to content

Commit

Permalink
Merge pull request #18 from pmcharrison/dev
Browse files Browse the repository at this point in the history
v0.12.1
  • Loading branch information
pmcharrison committed Nov 13, 2020
2 parents 44dfeb1 + ffdc495 commit 7d28ce9
Show file tree
Hide file tree
Showing 17 changed files with 362 additions and 23 deletions.
2 changes: 1 addition & 1 deletion DESCRIPTION
@@ -1,6 +1,6 @@
Package: hrep
Title: Harmony Representations
Version: 0.11.1
Version: 0.12.1
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.
Expand Down
3 changes: 3 additions & 0 deletions NAMESPACE
Expand Up @@ -164,6 +164,7 @@ S3method(represent,default)
S3method(represent,list)
S3method(represent,vec)
S3method(sample_rate,numeric)
S3method(save_wav,default)
S3method(save_wav_sox,coded_vec)
S3method(save_wav_sox,default)
S3method(save_wav_sox,fr_chord)
Expand Down Expand Up @@ -299,9 +300,11 @@ export(pi_chord_type)
export(pi_to_pc)
export(pitch)
export(play_sox)
export(play_wav)
export(register_chord_quality)
export(represent)
export(sample_rate)
export(save_wav)
export(save_wav_sox)
export(smooth_pc_spectrum)
export(smooth_pi_spectrum)
Expand Down
8 changes: 8 additions & 0 deletions NEWS.md
@@ -1,3 +1,11 @@
# hrep 0.12.1

- Exported `play_wav`.

# hrep 0.12.0

- Implemented `save_wav` and `play_wav`, a more streamlined and faster alternative to `save_wav_sox` and `play_wav_sox`.

# hrep 0.11.1

- Improved documentation for sparse_pi_spectrum, sparse_pc_spectrum, and sparse_fr_spectrum.
Expand Down
4 changes: 4 additions & 0 deletions R/play-sox.R
Expand Up @@ -4,6 +4,8 @@
#'
#' The sound is synthesised using \code{\link{save_wav_sox}}
#' and saved to a temporary file, which is then played from the R session.
#' This method is generally slower than \code{\link{play_wav}}
#' but it provides more features.
#'
#' @note
#' The command-line sound-processing program sox
Expand All @@ -14,6 +16,8 @@
#' @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}}.
#'
#' @seealso \code{\link{play_wav}}
#'
#' @export
play_sox <- function(x, ...) {
UseMethod("play_sox")
Expand Down
5 changes: 5 additions & 0 deletions R/save-wav-sox.R
Expand Up @@ -3,6 +3,9 @@
#' Saves object to a wav file using sox
#' (\url{http://sox.sourceforge.net/}).
#'
#' This method is generally slower than \code{\link{save_wav}}
#' but it provides more features.
#'
#' @note
#' The command-line sound-processing program sox
#' (\url{http://sox.sourceforge.net/})
Expand All @@ -17,6 +20,8 @@
#'
#' @param ... Parameters passed to methods.
#'
#' @seealso \code{\link{save_wav}}
#'
#' @rdname save_wav_sox
#' @export
save_wav_sox <- function(x, file, ...) {
Expand Down
3 changes: 1 addition & 2 deletions R/sparse-fr-spectrum.R
Expand Up @@ -48,8 +48,7 @@ is.sparse_fr_spectrum <- function(x) {
#' the second element should be labelled "amplitude",
#' and correspond to a numeric vector of amplitudes.
#'
#' @param ... Further arguments passed to \code{\link{expand_harmonics}()},
#' depending on the method invoked.
#' @inheritDotParams expand_harmonics
#'
#' @return An object of class \code{sparse_fr_spectrum}.
#'
Expand Down
3 changes: 1 addition & 2 deletions R/sparse-pc-spectrum.R
Expand Up @@ -55,8 +55,7 @@ is.sparse_pc_spectrum <- function(x) {
#'
#' @param x Input sonority.
#'
#' @param ... Further arguments passed to \code{\link{expand_harmonics}()},
#' depending on the method invoked.
#' @inheritDotParams expand_harmonics
#'
#' @return An object of class \code{sparse_pc_spectrum}.
#'
Expand Down
3 changes: 1 addition & 2 deletions R/sparse-pi-spectrum.R
Expand Up @@ -55,8 +55,7 @@ is.sparse_pi_spectrum <- function(x) {
#'
#' @param x Input sonority.
#'
#' @param ... Further arguments passed to \code{\link{expand_harmonics}()},
#' depending on the method invoked.
#' @inheritDotParams expand_harmonics
#'
#' @return An object of class \code{sparse_pi_spectrum}.
#'
Expand Down
164 changes: 157 additions & 7 deletions R/wave.R
Expand Up @@ -34,7 +34,8 @@ print.wave <- function(x, ...) {
#' The sample rate can be accessed using the \code{\link{sample_rate}} accessor.
#'
#' @param x Input object.
#' @param ... Arguments to be passed to \code{\link{sparse_fr_spectrum}}, as appropriate.
#'
#' @inheritDotParams expand_harmonics
#'
#' @rdname wave
#' @export
Expand All @@ -43,36 +44,84 @@ wave <- function(x, ...) {
}

#' @param length_sec (Numeric scalar) Length of the output wave, in seconds.
#'
#' @param sample_rate (Integerish scalar) The desired sample rate.
#'
#' @param rise_length
#' (Numeric scalar)
#' Chord fade-in time (seconds).
#'
#' @param fade_length
#' (Numeric scalar)
#' Chord fade-out time (seconds).
#'
#' @rdname wave
#' @export
wave.default <- function(x, length_sec = 1, sample_rate = 44100, ...) {
wave.default <- function(x,
length_sec = 1,
sample_rate = 44100,
rise_length = 0,
fade_length = 0,
...) {
x <- sparse_fr_spectrum(x, ...)
wave(x, length_sec = length_sec, sample_rate = sample_rate)
wave(x,
length_sec = length_sec,
rise_length = rise_length,
fade_length = fade_length,
sample_rate = sample_rate)
}

#' @export
wave.wave <- function(x, ...) x

#' @rdname wave
#' @export
wave.sparse_fr_spectrum <- function(x, length_sec = 1, sample_rate = 44100, ...) {
wave.sparse_fr_spectrum <- function(
x,
length_sec = 1,
sample_rate = 44100,
rise_length = 0,
fade_length = 0,
...
) {
checkmate::qtest(length_sec, "N1[0)")
checkmate::qtest(sample_rate, "X1[1)")
stopifnot(rise_length <= length_sec,
fade_length <= length_sec)
frequency <- freq(x)
amplitude <- amp(x)
num_samples <- sample_rate * length_sec
time <- seq(from = 0,
to = length_sec,
length.out = num_samples + 1)[- (num_samples + 1)]
mapply(
wave <- mapply(
function(frequency, amplitude) {
amplitude * sin(2 * pi * frequency * time)
}, frequency, amplitude
) %>%
rowMeans() %>%
.wave(sample_rate)
rowSums() %>%
.wave(sample_rate) %>%
add_fades(rise_length, fade_length, sample_rate, num_samples)
}

add_fades <- function(wave, rise_length, fade_length, sample_rate, num_samples) {
if (rise_length != 0) {
rise_n_samples <- round(rise_length * sample_rate)
stopifnot(rise_n_samples <= num_samples)
if (rise_n_samples > 0) {
ind <- 1:rise_n_samples
wave[ind] <- wave[ind] * seq(from = 0, to = 1, length.out = rise_n_samples)
}
}
if (fade_length != 0) {
fade_n_samples <- round(fade_length * sample_rate)
stopifnot(fade_n_samples <= num_samples)
if (fade_n_samples > 0) {
ind <- seq(to = num_samples, length.out = fade_n_samples)
wave[ind] <- wave[ind] * seq(from = 1, to = 0, length.out = fade_n_samples)
}
}
wave
}

#' @export
Expand All @@ -90,3 +139,104 @@ plot.wave <- function(x, ggplot = FALSE, xlab = "Time (seconds)", ylab = "Displa
plot(time, x, xlab = xlab, ylab = ylab, type = "l", ylim = ylim)
}
}

#' Save wav file
#'
#' Saves object to a wav file by converting to the \code{\link{wave}} representation
#' and then writing to a wav file.
#'
#' @param x Object to save; currently only individual chords are supported.
#' Chords are coerced to a \code{\link{wave}} representation.
#'
#' @param file (Character scalar) Output file.
#'
#' @param amplitude
#' (Numeric scalar)
#' The wave is multiplied by this number before exporting.
#' The resulting wave should fall completely within the range [-1, 1].
#'
#' @param bit_depth
#' (Integer scalar)
#' The bit depth of the exported audio.
#'
#' @param end_pad
#' (Numeric scalar)
#' Duration of silence (seconds) appended to the end of the audio file,
#' used to avoid clicks and other artifacts.
#'
#' @inheritParams wave
#' @inheritDotParams expand_harmonics
#'
#' @seealso \code{\link{save_wav_sox}}
#'
#' @rdname save_wav
#' @export
save_wav <- function(
x,
file,
amplitude = 0.1,
bit_depth = 16L,
length_sec = 1,
fade_length = 0.1,
rise_length = 0.1,
end_pad = 0.05,
...
) {
UseMethod("save_wav")
}

#' @export
save_wav.default <- function(
x,
file,
amplitude = 0.1,
bit_depth = 16,
length_sec = 1,
fade_length = 0.1,
rise_length = 0.1,
end_pad = 0.05,
...
) {
wave <- wave(
x,
length_sec = length_sec,
fade_length = fade_length,
rise_length = rise_length,
...
)
scale <- 2 ^ (bit_depth - 1)
vector <- round(as.numeric(wave) * amplitude * scale)
if (any(vector < - scale | vector >= scale)) {
stop(sprintf("the wave's maximum value was approximately %.2f times too big for exporting, ",
max(abs(vector / scale))),
"consider reducing amplitude by at least this value")
}
vector <- c(vector, rep(0, times = round(end_pad * sample_rate(wave))))
wave_2 <- tuneR::Wave(
left = vector,
right = vector,
samp.rate = sample_rate(wave),
bit = bit_depth
)
tuneR::writeWave(wave_2, file)
}

#' Play wav
#'
#' Plays a chord as a wave file.
#'
#' @param x Chord to save.
#'
#' @inheritParams tuneR::play
#' @inheritDotParams save_wav
#' @inheritDotParams expand_harmonics
#'
#' @seealso \code{\link{play_sox}}
#'
#' @export
play_wav <- function(x, player = "play", ...) {
file <- tempfile(fileext = ".wav")
save_wav(x, file, ...)
tuneR::play(file, player = player)
invisible(file.remove(file))
}
5 changes: 5 additions & 0 deletions man/play_sox.Rd

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

46 changes: 46 additions & 0 deletions man/play_wav.Rd

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

0 comments on commit 7d28ce9

Please sign in to comment.