Data music R functions

Here are some particular functions that I have found helpful for data music.

I use the following function to render PCM audio.

#' IEEE 753 double-precision floats (f64le)
write.pcm <- function(x, filename) {
  if (any(is.na(x)) || min(x) < -1 || max(x) > 1)
    stop('You must normalize and remove NAs.')

  dsp <- file(filename, open='w+b')
  writeBin(as.double(x), dsp)
  close(dsp)
}

I convert PCM audio to other audio formats with ffmpeg. You could turn the following code into a function.

SAMPLE.RATE <- 48000

x <- runif(SAMPLE.RATE) # One second of noise
write.pcm(x, 'noise.pcm')

system2('ffmpeg', '-f', 'f64le', '-acodec', 'pcm_f64le', '-ar',
        SAMPLE.RATE,
        '-i', 'noise.pcm', '-y', 'noise.ogg')
system2('mplayer', 'noise.ogg')

This is also how I combine video and music.

fps <- 4
system(paste('ffmpeg -f f64le -acodec pcm_f64le',
             '-ar', SAMPLE.RATE, '-i data-music.pcm',
             '-thread_queue_size 100',
             '-r', fps, '-i data-video-frame-%03d.png',
             '-pix_fmt rgb4_byte -r', fps,
             '-y data-music-video.mkv'))

Starting with a wave from any arbitrary source, I can interpolate it to create a wave of similar sound at a different frequency.

#' Shift a wave to be a particular frequency.
interpolate.pitch <- function(wave, freq, symmetric=FALSE,
                              duration=SECOND, wavelengths=1, bitrate=SECOND) {
  atom <- approx(wave, n=wavelengths*bitrate/freq)$y
  l <- duration/length(atom)
  if (symmetric)
    y <- rep(c(atom, rev(atom)), ceiling(l/2))
  else
    y <- rep(atom, ceiling(l))
  y[1:duration]
}

Here's are examples with sunspots and lynx trappings.

sunspots.norm <- 2*sunspots/max(sunspots)-1
note.sun <- function(ratio)
  interpolate.pitch(sunspots.norm, 440*ratio,
                    duration=3/2*SECOND, wavelength=21.5, bitrate=SECOND)

lynx.norm <- 2*lynx/max(lynx)-1
note.lynx <- function(ratio)
  interpolate.pitch(lynx.norm, 440*ratio,
                    duration=3/2*SECOND, wavelength=11.5, bitrate=SECOND)

data.music <- c(note.sun(1),
                note.sun(3/2),
                note.sun(4/3),
                note.lynx(1),
                note.lynx(3/2),
                note.lynx(4/3))

write.pcm(data.music, 'data-music.pcm')
system(paste('ffmpeg -f f64le -acodec pcm_f64le',
              '-ar', SECOND, '-i data-music.pcm',
              '-y data-music.wav'))
system('mplayer data-music.wav')