diff --git a/DESCRIPTION b/DESCRIPTION index a2e8600..6ea7ea5 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: hrep Title: Harmony Representations -Version: 0.16.1 +Version: 0.20.0 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. @@ -18,7 +18,7 @@ Suggests: testthat (>= 2.1.0), covr (>= 3.2.0), ggplot2 (>= 3.0.0) -RoxygenNote: 7.2.3 +RoxygenNote: 7.3.1 Imports: plyr (>= 1.8.4), Rdpack (>= 0.9.0), diff --git a/NEWS.md b/NEWS.md index 4379f60..acc8493 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,11 @@ +# hrep 0.20.0 + +# hrep 0.19.0 + +# hrep 0.18.0 + +# hrep 0.17.0 + # hrep 0.16.1 - Remove unnecessary info messages. diff --git a/R/expand-harmonics.R b/R/expand-harmonics.R index 57b2bde..216a753 100644 --- a/R/expand-harmonics.R +++ b/R/expand-harmonics.R @@ -13,7 +13,7 @@ #' Number of harmonics (including the fundamental) to which #' each tone should be expanded. #' -#' @param roll_off (Numeric scalar) Parametrises the amount of amplitude roll-off +#' @param roll_off_dB (Numeric scalar) Parametrises the amount of amplitude roll-off #' in the harmonics, with greater values corresponding to higher roll-off. #' #' @param digits @@ -22,16 +22,20 @@ #' @param label_harmonics #' If TRUE, then the harmonics in the resulting spectrum are labelled with their harmonic numbers. #' +#' @param pseudo_octave +#' The octave ratio for stretching and compressing harmonics, defaults to 2.0. +#' #' @rdname expand_harmonics #' #' @inheritParams collapse_summing_amplitudes #' @export expand_harmonics <- function(x, num_harmonics = 11L, - roll_off = 1, + roll_off_dB = 1, digits = 6, label_harmonics = FALSE, - coherent = FALSE) { + coherent = FALSE, + pseudo_octave = 2.0) { UseMethod("expand_harmonics") } @@ -39,13 +43,14 @@ expand_harmonics <- function(x, #' @export expand_harmonics.sparse_fr_spectrum <- function(x, num_harmonics = 11L, - roll_off = 1, + roll_off_dB = 1, digits = 6, label_harmonics = FALSE, - coherent = FALSE) { + coherent = FALSE, + pseudo_octave = 2.0) { expand_harmonics(sparse_pi_spectrum(x), num_harmonics = num_harmonics, - roll_off = roll_off, + roll_off_dB = roll_off_dB, digits = digits, label_harmonics = label_harmonics, coherent = coherent) %>% @@ -56,18 +61,20 @@ expand_harmonics.sparse_fr_spectrum <- function(x, #' @export expand_harmonics.sparse_pi_spectrum <- function(x, num_harmonics = 11L, - roll_off = 1, + roll_off_dB = 1, digits = 6, label_harmonics = FALSE, - coherent = FALSE) { - template <- pi_harmonic_template(num_harmonics, roll_off) + coherent = FALSE, + pseudo_octave = 2.0) { purrr::map2(pitch(x), amp(x), function(pitch, amp) { + n <- seq_len(num_harmonics) + f0 <- midi_to_freq(pitch) df <- data.frame( - x = pitch + template$interval, - y = amp * template$amplitude + x = freq_to_midi(f0 * pseudo_octave ^ log2(n)), + y = 1 * 10 ^ ( -roll_off_dB * log2(n) / 20) ) - if (label_harmonics) df$labels <- seq_along(template$interval) + if (label_harmonics) df$labels <- seq_along(df$x) df }) %>% collapse_summing_amplitudes(digits = digits, coherent = coherent) %>% @@ -78,19 +85,14 @@ expand_harmonics.sparse_pi_spectrum <- function(x, #' @export expand_harmonics.pi_chord <- function(x, num_harmonics = 11L, - roll_off = 1, + roll_off_dB = 1, digits = 6, label_harmonics = FALSE, - coherent = FALSE) { + coherent = FALSE, + pseudo_octave = 2.0) { sparse_pi_spectrum(x, num_harmonics = num_harmonics, - roll_off = roll_off, + roll_off_dB = roll_off_dB, digits = digits, label_harmonics = label_harmonics) } - -pi_harmonic_template <- function(num_harmonics, roll_off, digits = 6) { - tibble::tibble(n = seq_len(num_harmonics), - interval = 12 * log(.data$n, base = 2), - amplitude = 1 / (.data$n ^ roll_off)) -} diff --git a/R/smooth-pc-spectrum.R b/R/smooth-pc-spectrum.R index 6e90dfe..1ca245f 100644 --- a/R/smooth-pc-spectrum.R +++ b/R/smooth-pc-spectrum.R @@ -75,7 +75,7 @@ smooth_pc_spectrum <- function( ..., sigma = 6.83, num_harmonics = 11L, - roll_off = 1, + roll_off_dB = 1, coherent = FALSE ) { UseMethod("smooth_pc_spectrum") @@ -88,12 +88,12 @@ smooth_pc_spectrum.default <- function( ..., sigma = 6.83, num_harmonics = 11L, - roll_off = 1, + roll_off_dB = 1, coherent = FALSE ) { smooth_pc_spectrum(sparse_pc_spectrum(x, num_harmonics = num_harmonics, - roll_off = roll_off, + roll_off_dB = roll_off_dB, coherent = coherent), sigma = sigma, coherent = coherent, diff --git a/R/smooth-pi-spectrum.R b/R/smooth-pi-spectrum.R index 8133cbc..739c8f5 100644 --- a/R/smooth-pi-spectrum.R +++ b/R/smooth-pi-spectrum.R @@ -79,12 +79,12 @@ smooth_pi_spectrum <- function(x, sigma = 6.83, ...) { smooth_pi_spectrum.default <- function(x, sigma = 6.83, num_harmonics = 11L, - roll_off = 1, + roll_off_dB = 1, coherent = FALSE, ...) { smooth_pi_spectrum(sparse_pi_spectrum(x, num_harmonics = num_harmonics, - roll_off = roll_off, + roll_off_dB = roll_off_dB, coherent = coherent, ...), sigma = sigma, diff --git a/R/sparse-spectrum.R b/R/sparse-spectrum.R index 253934f..df03599 100644 --- a/R/sparse-spectrum.R +++ b/R/sparse-spectrum.R @@ -78,14 +78,24 @@ set_labels.sparse_spectrum <- function(x, labels) { } #' @export -plot.sparse_spectrum <- function(x, ggplot = FALSE, xlim = NULL, ...) { +plot.sparse_spectrum <- function(x, cochlea=FALSE, ggplot = FALSE, xlim = NULL, + trans='identity', ...) { df <- as.data.frame(x) + if (cochlea) { + greenwood <- function(frequency) { + 100 * (1 - (log10( frequency / 165.4 + 0.88 ) / 2.1)) + } + df$x = greenwood(df$x) + x_label = 'Cochlea (%)' + } else { + x_label = x_lab(x) + } if (ggplot) { assert_installed("ggplot2") ggplot2::ggplot(df, ggplot2::aes_string(x = "x", xend = "x", y = 0, yend = "y")) + ggplot2::geom_segment() + - ggplot2::scale_x_continuous(x_lab(x), limits = xlim) + + ggplot2::scale_x_continuous(x_label, limits = xlim, transform=trans) + ggplot2::scale_y_continuous(y_lab(x)) } else { n <- nrow(df) @@ -95,7 +105,7 @@ plot.sparse_spectrum <- function(x, ggplot = FALSE, xlim = NULL, ...) { df2$x[I + 1:3] <- df$x[i] df2$y[I + 2L] <- df$y[i] } - plot(df2$x, df2$y, xlab = x_lab(x), ylab = y_lab(x), + plot(df2$x, df2$y, xlab = x_label, ylab = y_lab(x), type = "l", xlim = xlim, ...) if (!is.null(df$labels)) { for (i in seq_len(nrow(df))) { diff --git a/man/expand_harmonics.Rd b/man/expand_harmonics.Rd index 11aa277..623da60 100644 --- a/man/expand_harmonics.Rd +++ b/man/expand_harmonics.Rd @@ -10,37 +10,41 @@ expand_harmonics( x, num_harmonics = 11L, - roll_off = 1, + roll_off_dB = 1, digits = 6, label_harmonics = FALSE, - coherent = FALSE + coherent = FALSE, + pseudo_octave = 2 ) \method{expand_harmonics}{sparse_fr_spectrum}( x, num_harmonics = 11L, - roll_off = 1, + roll_off_dB = 1, digits = 6, label_harmonics = FALSE, - coherent = FALSE + coherent = FALSE, + pseudo_octave = 2 ) \method{expand_harmonics}{sparse_pi_spectrum}( x, num_harmonics = 11L, - roll_off = 1, + roll_off_dB = 1, digits = 6, label_harmonics = FALSE, - coherent = FALSE + coherent = FALSE, + pseudo_octave = 2 ) \method{expand_harmonics}{pi_chord}( x, num_harmonics = 11L, - roll_off = 1, + roll_off_dB = 1, digits = 6, label_harmonics = FALSE, - coherent = FALSE + coherent = FALSE, + pseudo_octave = 2 ) } \arguments{ @@ -54,7 +58,7 @@ Should be of class Number of harmonics (including the fundamental) to which each tone should be expanded.} -\item{roll_off}{(Numeric scalar) Parametrises the amount of amplitude roll-off +\item{roll_off_dB}{(Numeric scalar) Parametrises the amount of amplitude roll-off in the harmonics, with greater values corresponding to higher roll-off.} \item{digits}{Number of digits to which each partial's MIDI pitch should be rounded.} @@ -66,6 +70,8 @@ assuming coherent summation, where the amplitudes simply add together (default is \code{FALSE}). Otherwise incoherent summation is used, where the amplitudes are squared, added, then square rooted.} + +\item{pseudo_octave}{The octave ratio for stretching and compressing harmonics, defaults to 2.0.} } \description{ Expands each tone in an object into its implied harmonics. diff --git a/man/play_wav.Rd b/man/play_wav.Rd index 783c0e7..0c4f4f1 100644 --- a/man/play_wav.Rd +++ b/man/play_wav.Rd @@ -33,10 +33,11 @@ Chord fade-out time (seconds).} \item{\code{num_harmonics}}{(Integerish scalar) Number of harmonics (including the fundamental) to which each tone should be expanded.} - \item{\code{roll_off}}{(Numeric scalar) Parametrises the amount of amplitude roll-off + \item{\code{roll_off_dB}}{(Numeric scalar) Parametrises the amount of amplitude roll-off in the harmonics, with greater values corresponding to higher roll-off.} \item{\code{digits}}{Number of digits to which each partial's MIDI pitch should be rounded.} \item{\code{label_harmonics}}{If TRUE, then the harmonics in the resulting spectrum are labelled with their harmonic numbers.} + \item{\code{pseudo_octave}}{The octave ratio for stretching and compressing harmonics, defaults to 2.0.} \item{\code{coherent}}{Whether the amplitudes from different spectral components should be combined assuming coherent summation, where the amplitudes simply add together (default is \code{FALSE}). diff --git a/man/save_wav.Rd b/man/save_wav.Rd index a990c0e..c9b6fa4 100644 --- a/man/save_wav.Rd +++ b/man/save_wav.Rd @@ -47,10 +47,11 @@ used to avoid clicks and other artifacts.} \item{\code{num_harmonics}}{(Integerish scalar) Number of harmonics (including the fundamental) to which each tone should be expanded.} - \item{\code{roll_off}}{(Numeric scalar) Parametrises the amount of amplitude roll-off + \item{\code{roll_off_dB}}{(Numeric scalar) Parametrises the amount of amplitude roll-off in the harmonics, with greater values corresponding to higher roll-off.} \item{\code{digits}}{Number of digits to which each partial's MIDI pitch should be rounded.} \item{\code{label_harmonics}}{If TRUE, then the harmonics in the resulting spectrum are labelled with their harmonic numbers.} + \item{\code{pseudo_octave}}{The octave ratio for stretching and compressing harmonics, defaults to 2.0.} \item{\code{coherent}}{Whether the amplitudes from different spectral components should be combined assuming coherent summation, where the amplitudes simply add together (default is \code{FALSE}). diff --git a/man/smooth_pc_spectrum.Rd b/man/smooth_pc_spectrum.Rd index 570d246..035021e 100644 --- a/man/smooth_pc_spectrum.Rd +++ b/man/smooth_pc_spectrum.Rd @@ -11,7 +11,7 @@ smooth_pc_spectrum( ..., sigma = 6.83, num_harmonics = 11L, - roll_off = 1, + roll_off_dB = 1, coherent = FALSE ) @@ -20,7 +20,7 @@ smooth_pc_spectrum( ..., sigma = 6.83, num_harmonics = 11L, - roll_off = 1, + roll_off_dB = 1, coherent = FALSE ) @@ -41,7 +41,7 @@ perceptual blurring. Defaults to 6.83 cents, after Number of harmonics (including the fundamental) to which each tone should be expanded.} -\item{roll_off}{(Numeric scalar) Parametrises the amount of amplitude roll-off +\item{roll_off_dB}{(Numeric scalar) Parametrises the amount of amplitude roll-off in the harmonics, with greater values corresponding to higher roll-off.} \item{coherent}{Whether the amplitudes from different spectral components should be combined diff --git a/man/smooth_pi_spectrum.Rd b/man/smooth_pi_spectrum.Rd index 2cb8295..6d691ae 100644 --- a/man/smooth_pi_spectrum.Rd +++ b/man/smooth_pi_spectrum.Rd @@ -12,7 +12,7 @@ smooth_pi_spectrum(x, sigma = 6.83, ...) x, sigma = 6.83, num_harmonics = 11L, - roll_off = 1, + roll_off_dB = 1, coherent = FALSE, ... ) @@ -34,7 +34,7 @@ perceptual blurring. Defaults to 6.83 cents, after Number of harmonics (including the fundamental) to which each tone should be expanded.} -\item{roll_off}{(Numeric scalar) Parametrises the amount of amplitude roll-off +\item{roll_off_dB}{(Numeric scalar) Parametrises the amount of amplitude roll-off in the harmonics, with greater values corresponding to higher roll-off.} \item{coherent}{Whether the amplitudes from different spectral components should be combined diff --git a/man/sparse_fr_spectrum.Rd b/man/sparse_fr_spectrum.Rd index a9732bd..e524f0e 100644 --- a/man/sparse_fr_spectrum.Rd +++ b/man/sparse_fr_spectrum.Rd @@ -39,10 +39,11 @@ and correspond to a numeric vector of amplitudes. \item{\code{num_harmonics}}{(Integerish scalar) Number of harmonics (including the fundamental) to which each tone should be expanded.} - \item{\code{roll_off}}{(Numeric scalar) Parametrises the amount of amplitude roll-off + \item{\code{roll_off_dB}}{(Numeric scalar) Parametrises the amount of amplitude roll-off in the harmonics, with greater values corresponding to higher roll-off.} \item{\code{digits}}{Number of digits to which each partial's MIDI pitch should be rounded.} \item{\code{label_harmonics}}{If TRUE, then the harmonics in the resulting spectrum are labelled with their harmonic numbers.} + \item{\code{pseudo_octave}}{The octave ratio for stretching and compressing harmonics, defaults to 2.0.} \item{\code{coherent}}{Whether the amplitudes from different spectral components should be combined assuming coherent summation, where the amplitudes simply add together (default is \code{FALSE}). diff --git a/man/sparse_pc_spectrum.Rd b/man/sparse_pc_spectrum.Rd index 3d6b13c..de6040d 100644 --- a/man/sparse_pc_spectrum.Rd +++ b/man/sparse_pc_spectrum.Rd @@ -27,10 +27,11 @@ sparse_pc_spectrum(x, ...) \item{\code{num_harmonics}}{(Integerish scalar) Number of harmonics (including the fundamental) to which each tone should be expanded.} - \item{\code{roll_off}}{(Numeric scalar) Parametrises the amount of amplitude roll-off + \item{\code{roll_off_dB}}{(Numeric scalar) Parametrises the amount of amplitude roll-off in the harmonics, with greater values corresponding to higher roll-off.} \item{\code{digits}}{Number of digits to which each partial's MIDI pitch should be rounded.} \item{\code{label_harmonics}}{If TRUE, then the harmonics in the resulting spectrum are labelled with their harmonic numbers.} + \item{\code{pseudo_octave}}{The octave ratio for stretching and compressing harmonics, defaults to 2.0.} }} \item{coherent}{Whether the amplitudes from different spectral components should be combined diff --git a/man/sparse_pi_spectrum.Rd b/man/sparse_pi_spectrum.Rd index 0c230bf..40dd482 100644 --- a/man/sparse_pi_spectrum.Rd +++ b/man/sparse_pi_spectrum.Rd @@ -30,10 +30,11 @@ sparse_pi_spectrum(x, ...) \item{\code{num_harmonics}}{(Integerish scalar) Number of harmonics (including the fundamental) to which each tone should be expanded.} - \item{\code{roll_off}}{(Numeric scalar) Parametrises the amount of amplitude roll-off + \item{\code{roll_off_dB}}{(Numeric scalar) Parametrises the amount of amplitude roll-off in the harmonics, with greater values corresponding to higher roll-off.} \item{\code{digits}}{Number of digits to which each partial's MIDI pitch should be rounded.} \item{\code{label_harmonics}}{If TRUE, then the harmonics in the resulting spectrum are labelled with their harmonic numbers.} + \item{\code{pseudo_octave}}{The octave ratio for stretching and compressing harmonics, defaults to 2.0.} }} \item{amplitude}{(Numeric vector) diff --git a/man/wave.Rd b/man/wave.Rd index 0cf9c4c..93dd88b 100644 --- a/man/wave.Rd +++ b/man/wave.Rd @@ -36,10 +36,11 @@ wave(x, ...) \item{\code{num_harmonics}}{(Integerish scalar) Number of harmonics (including the fundamental) to which each tone should be expanded.} - \item{\code{roll_off}}{(Numeric scalar) Parametrises the amount of amplitude roll-off + \item{\code{roll_off_dB}}{(Numeric scalar) Parametrises the amount of amplitude roll-off in the harmonics, with greater values corresponding to higher roll-off.} \item{\code{digits}}{Number of digits to which each partial's MIDI pitch should be rounded.} \item{\code{label_harmonics}}{If TRUE, then the harmonics in the resulting spectrum are labelled with their harmonic numbers.} + \item{\code{pseudo_octave}}{The octave ratio for stretching and compressing harmonics, defaults to 2.0.} \item{\code{coherent}}{Whether the amplitudes from different spectral components should be combined assuming coherent summation, where the amplitudes simply add together (default is \code{FALSE}). diff --git a/tests/testthat/test-expand_harmonics.R b/tests/testthat/test-expand_harmonics.R index 2023abb..1491aa3 100644 --- a/tests/testthat/test-expand_harmonics.R +++ b/tests/testthat/test-expand_harmonics.R @@ -36,14 +36,14 @@ test_that("misc", { test_that("roll-off", { pi_chord(0) %>% - expand_harmonics(num_harmonics = 6, roll_off = 2) %>% + expand_harmonics(num_harmonics = 6, roll_off_dB = 2) %>% {amp(.)} %>% expect_equal(c(1, 1 / 2 ^ 2, 1 / 3 ^ 2, 1 / 4 ^ 2, 1 / 5 ^ 2, 1 / 6 ^ 2)) }) test_that("MIDI transposition", { pi_chord(0) %>% - expand_harmonics(num_harmonics = 6, roll_off = 2) %>% + expand_harmonics(num_harmonics = 6, roll_off_dB = 2) %>% {pitch(.)} %>% expect_equal(c(10, 22, 29, 34, 38, 41), tolerance = 1) @@ -79,3 +79,43 @@ test_that("rounding", { c(0, 7) %>% sparse_pi_spectrum(digits = 0) ) }) +test_that('octave ratio stretches and compresses',{ + harmonic_default_result <- 60 %>% + sparse_pi_spectrum(num_harmonics=10) %>% + pitch + expect_equal(harmonic_default_result[1], 60) + expect_equal(harmonic_default_result[2], 60+12) + expect_equal(harmonic_default_result[4], 60+24) + expect_equal(harmonic_default_result[8], 60+36) + + harmonic_explicit_result <- 60 %>% + sparse_pi_spectrum(num_harmonics=10, pseudo_octave = 2.0) %>% + pitch + expect_equal(harmonic_explicit_result[1], 60) + expect_equal(harmonic_explicit_result[2], 60+12) + expect_equal(harmonic_explicit_result[4], 60+24) + expect_equal(harmonic_explicit_result[8], 60+36) + + compressed_result <- 60 %>% + sparse_pi_spectrum(num_harmonics=10, pseudo_octave = 1.9) %>% + pitch + expect_equal(compressed_result[1], 60) + expect_equal(compressed_result[2], + freq_to_midi(midi_to_freq(60)*1.9^log2(2))) + expect_equal(compressed_result[4], + freq_to_midi(midi_to_freq(60)*1.9^log2(4))) + expect_equal(compressed_result[8], + freq_to_midi(midi_to_freq(60)*1.9^log2(8))) + + stretcheded_result <- 60 %>% + sparse_pi_spectrum(num_harmonics=10, pseudo_octave = 2.1) %>% + pitch + expect_equal(stretcheded_result[1], 60) + expect_equal(stretcheded_result[2], + freq_to_midi(midi_to_freq(60)*2.1^log2(2))) + expect_equal(stretcheded_result[4], + freq_to_midi(midi_to_freq(60)*2.1^log2(4))) + expect_equal(stretcheded_result[8], + freq_to_midi(midi_to_freq(60)*2.1^log2(8))) + +}) diff --git a/tests/testthat/test-gaussian_filter.R b/tests/testthat/test-gaussian_filter.R index 6d16e1d..89599e0 100644 --- a/tests/testthat/test-gaussian_filter.R +++ b/tests/testthat/test-gaussian_filter.R @@ -4,7 +4,7 @@ test_that("worked example", { fr_chord(100) %>% filter_spectrum_gaussian(location = c(100, 1000, 2000), width = 100, - roll_off = 0, num_harmonics = 30) %>% + roll_off_dB = 0, num_harmonics = 30) %>% tibble::as_tibble() %>% (function(x) x[x$y > 0.2, ]) %>% expect_equal(tibble::tribble( diff --git a/tests/testthat/test-sparse_fr_spectrum.R b/tests/testthat/test-sparse_fr_spectrum.R new file mode 100644 index 0000000..b7654d0 --- /dev/null +++ b/tests/testthat/test-sparse_fr_spectrum.R @@ -0,0 +1,42 @@ +context("test-sparse_fr_spectrum") + +test_that('octave ratio stretches and compresses',{ + harmonic_default_result <- 60 %>% + sparse_fr_spectrum(num_harmonics=10) %>% + freq %>% freq_to_midi + expect_equal(harmonic_default_result[1], 60) + expect_equal(harmonic_default_result[2], 60+12) + expect_equal(harmonic_default_result[4], 60+24) + expect_equal(harmonic_default_result[8], 60+36) + + harmonic_explicit_result <- 60 %>% + sparse_fr_spectrum(num_harmonics=10, pseudo_octave = 2.0) %>% + freq %>% freq_to_midi + expect_equal(harmonic_explicit_result[1], 60) + expect_equal(harmonic_explicit_result[2], 60+12) + expect_equal(harmonic_explicit_result[4], 60+24) + expect_equal(harmonic_explicit_result[8], 60+36) + + compressed_result <- 60 %>% + sparse_fr_spectrum(num_harmonics=10, pseudo_octave = 1.9) %>% + freq %>% freq_to_midi + expect_equal(compressed_result[1], 60) + expect_equal(compressed_result[2], + freq_to_midi(midi_to_freq(60)*1.9^log2(2))) + expect_equal(compressed_result[4], + freq_to_midi(midi_to_freq(60)*1.9^log2(4))) + expect_equal(compressed_result[8], + freq_to_midi(midi_to_freq(60)*1.9^log2(8))) + + stretcheded_result <- 60 %>% + sparse_fr_spectrum(num_harmonics=10, pseudo_octave = 2.1) %>% + freq %>% freq_to_midi + expect_equal(stretcheded_result[1], 60) + expect_equal(stretcheded_result[2], + freq_to_midi(midi_to_freq(60)*2.1^log2(2))) + expect_equal(stretcheded_result[4], + freq_to_midi(midi_to_freq(60)*2.1^log2(4))) + expect_equal(stretcheded_result[8], + freq_to_midi(midi_to_freq(60)*2.1^log2(8))) + +})