danigb/timestretch

View on GitHub
lib/index.js

Summary

Maintainability
A
0 mins
Test Coverage
'use strict'

/**
 * Copy `len` bytes generated by a function to `array` starting at `pos`
 */
function copy (len, array, pos, fn) {
  for (var i = 0; i < len; i++) {
    array[pos + i] = fn(i)
  }
}

function stretch (ac, input, scale, options) {
  // OPTIONS
  var opts = options || {}
  // Processing sequence size (100 msec with 44100Hz sample rate)
  var seqSize = opts.seqSize || 4410
  // Overlapping size (20 msec)
  var overlap = opts.overlap || 882
  // Best overlap offset seeking window (15 msec)
  // var seekWindow = opts.seekWindow || 662

  // The theoretical start of the next sequence
  var nextOffset = Math.round(seqSize / scale)

  // Setup the buffers
  var numSamples = input.length
  var output = ac.createBuffer(1, numSamples * scale, input.sampleRate)
  var inL = input.getChannelData(0)
  var outL = output.getChannelData(0)

  // STATE
  // where to read then next sequence
  var read = 0
  // where to write the next sequence
  var write = 0
  // where to read the next fadeout
  var readOverlap = 0

  while (numSamples - read > seqSize) {
    // write the first overlap
    copy(overlap, outL, write, function (i) {
      var fadeIn = i / overlap
      var fadeOut = 1 - fadeIn
      // Mix the begin of the new sequence with the tail of the sequence last
      return (inL[read + i] * fadeIn + inL[readOverlap + i] * fadeOut) / 2
    })
    copy(seqSize - overlap, outL, write + overlap, function (i) {
      // Copy the tail of the sequence
      return inL[read + overlap + i]
    })
    // the next overlap is after this sequence
    readOverlap += read + seqSize
    // the next sequence is after the nextOffset
    read += nextOffset
    // we wrote a complete sequence
    write += seqSize
  }

  return output
}

module.exports = { stretch: stretch }