ola/index.js

/**
 * > Add and overlap timestretch algorithm
 *
 * The overlap and add is the simplest, cheaper (in terms of computation) and
 * less quality timestretch algorithm. It changes the length of a buffer without
 * changing it's pitch.
 *
 * [![npm install dsp-ola](https://nodei.co/npm/dsp-ola.png?mini=true)](https://npmjs.org/package/dsp-ola/)
 *
 * This is part of [dsp-kit](https://github.com/oramics/dsp-kit)
 *
 * @example
 * var ola = require('dsp-ola')
 * const stretch = ola.overlapAdd({ size: 1024 })
 * const halfSize = stretch(0.5, audioBuffer)
 *
 * @example
 * var dsp = require('dsp-kit')
 *
 * @module ola
 */
import { zeros, fill, add, mult } from 'dsp-array'
const cos = Math.cos
const PI2 = 2 * Math.PI

const hamming = () => (n, N) => 0.54 - 0.46 * cos(PI2 * n / (N - 1))

/**
 * Create a timestretch function using an overlap and add algorithm
 *
 * @param {Object} options
 * @return {Function} the timestretch function
 * @example
 * const stretch = ola.overlapAdd()
 * stretch(0.5, audio) // => a new audio buffer half of the length
 */
export function overlapAdd (options = {}) {
  const { size = 1024, hop = size / 2 } = options
  const window = fill(size, options.window || hamming())
  const frame = zeros(size)

  return function (factor, src, dest) {
    var frames
    // we change hop size in source to change the size of the dest
    const srcHopSize = Math.floor(hop / factor)
    const srcLen = src.length
    if (!dest) {
      // if not dest, create one
      frames = Math.floor(srcLen / srcHopSize)
      dest = zeros(frames * hop)
    } else {
      frames = Math.floor(dest.length / hop)
    }

    let read, write
    for (var i = 0; i < frames; i++) {
      read = src.subarray(i * srcHopSize, i * srcHopSize + size)
      write = src.subarray(i * hop, i * hop + size)
      mult(size, read, window, frame)
      add(size, frame, write, write)
    }
    return dest
  }
}