-
Notifications
You must be signed in to change notification settings - Fork 3
/
save-wav-sox.R
141 lines (134 loc) · 4.23 KB
/
save-wav-sox.R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
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()
}