UNPKG

ciseaux

Version:

utility to chop an audio buffer

276 lines (238 loc) 7.31 kB
"use strict"; /* eslint-disable */ var self = global.self || {}; function render() { self.repository = []; self.onmessage = function (e) { switch (e.data.type) { case "transfer": self.repository[e.data.data] = e.data.buffers.map(function (buffer) { return new Float32Array(buffer); }); break; case "dispose": delete self.repository[e.data.data]; break; case "render": self.startRendering(e.data.tape, e.data.callbackId); break; default: // do nothing } }; self.startRendering = function (tape, callbackId) { var destination = self.allocData(tape); var buffers = destination.map(function (array) { return array.buffer; }); self.render(tape, destination); self.postMessage({ callbackId: callbackId, buffers: buffers }, buffers); }; self.allocData = function (tape) { var data = new Array(tape.numberOfChannels); var length = Math.floor(tape.duration * tape.sampleRate); for (var i = 0; i < data.length; i++) { data[i] = new Float32Array(length); } return data; }; self.render = function (tape, destination) { for (var i = 0; i < tape.tracks.length; i++) { self.renderTrack(i, tape.tracks[i], destination, tape.sampleRate); } }; self.renderTrack = function (trackNum, fragments, destination, sampleRate) { var usePan = fragments.some(function (fragment) { return fragment.pan !== 0; }); var pos = 0; for (var i = 0, imax = fragments.length; i < imax; i++) { var fragment = fragments[i]; var source = self.repository[fragment.data]; var duration = (fragment.endTime - fragment.beginTime) / fragment.pitch; var length = Math.floor(duration * sampleRate); if (!source) { pos += length; continue; } var begin = Math.floor(fragment.beginTime * sampleRate); var end = Math.floor(fragment.endTime * sampleRate); var srcCh = source.length; var dstCh = destination.length; var srcSub = self.subarray(source, begin, end); var dstSub = self.subarray(destination, pos, pos + length); var pitch = fragment.pitch; /** TODO: implements if (fragment.stretch) { srcSub = self.stretch(srcSub, length); pitch = 1; } **/ var canSimpleCopy = trackNum === 0 && pitch === 1 && !usePan && fragment.gain === 1 && !fragment.reverse && srcCh <= dstCh && srcSub[0].length === dstSub[0].length; if (canSimpleCopy) { self.mix[srcSub.length + "->" + dstSub.length](srcSub, dstSub); } else { self.process(srcSub, dstSub, { gain: fragment.gain, pan: usePan ? Math.max(-1, Math.min(fragment.pan, +1)) : null, reverse: !!fragment.reverse }); } pos += length; } }; self.subarray = function (array, begin, end) { var subarray = new Array(array.length); for (var i = 0; i < subarray.length; i++) { subarray[i] = array[i].subarray(begin, end); } return subarray; }; self.process = function (src, dst, opts) { var samples = new Array(src.length); var srcCh = src.length; var dstCh = dst.length; var mixCh; var srcLength = src[0].length; var dstLength = dst[0].length; var factor = (srcLength - 1) / (dstLength - 1); var index, step, ch, mix, l, r; if (opts.pan !== null) { l = Math.cos((opts.pan + 1) * 0.25 * Math.PI); r = Math.sin((opts.pan + 1) * 0.25 * Math.PI); mixCh = Math.max(srcCh, 2); } else { mixCh = srcCh; } mix = self.mix1[mixCh + "->" + dstCh] || self.mix1.nop; if (opts.reverse) { index = dst[0].length - 1; step = -1; } else { index = 0; step = +1; } for (var i = 0; i < dstLength; i++, index += step) { var x0 = i * factor; var i0 = x0 | 0; var i1 = Math.min(i0 + 1, srcLength - 1); for (ch = 0; ch < srcCh; ch++) { samples[ch] = src[ch][i0] + Math.abs(x0 - i0) * (src[ch][i1] - src[ch][i0]); } if (opts.pan !== null) { samples = self.pan[srcCh](samples, l, r); } var values = mix(samples); for (ch = 0; ch < dstCh; ch++) { dst[ch][index] += (values[ch] || 0) * opts.gain; } } }; self.pan = []; self.pan[1] = function (src, l, r) { return [src[0] * l, src[0] * r]; }; self.pan[2] = function (src, l, r) { var x = (src[0] + src[1]) * 0.5; return [x * l, x * r]; }; self.pan[4] = function (src, l, r) { var x = (src[0] + src[1]) * 0.5; var y = (src[2] + src[3]) * 0.5; return [x * l, x * r, y * l, y * r]; }; self.pan[6] = function (src, l, r) { var x = (src[0] + src[1]) * 0.5; var y = (src[4] + src[5]) * 0.5; return [x * l, x * r, src[2], src[3], y * l, y * r]; }; self.mix = {}; self.mix["1->1"] = function (src, dst) { dst[0].set(src[0]); }; self.mix["1->2"] = function (src, dst) { dst[0].set(src[0]); dst[1].set(src[0]); }; self.mix["1->4"] = function (src, dst) { dst[0].set(src[0]); dst[1].set(src[0]); }; self.mix["1->6"] = function (src, dst) { dst[2].set(src[0]); }; self.mix["2->2"] = function (src, dst) { dst[0].set(src[0]); dst[1].set(src[1]); }; self.mix["2->4"] = function (src, dst) { dst[0].set(src[0]); dst[1].set(src[1]); }; self.mix["2->6"] = function (src, dst) { dst[0].set(src[0]); dst[1].set(src[1]); }; self.mix["4->4"] = function (src, dst) { dst[0].set(src[0]); dst[1].set(src[1]); dst[2].set(src[2]); dst[3].set(src[3]); }; self.mix["4->6"] = function (src, dst) { dst[0].set(src[0]); dst[1].set(src[1]); dst[4].set(src[2]); dst[5].set(src[3]); }; self.mix["6->6"] = function (src, dst) { dst[0].set(src[0]); dst[1].set(src[1]); dst[2].set(src[2]); dst[3].set(src[3]); dst[4].set(src[4]); dst[5].set(src[5]); }; self.mix1 = {}; self.mix1.nop = function (src) { return src; }; self.mix1["1->2"] = function (src) { return [src[0], src[0]]; }; self.mix1["1->4"] = function (src) { return [src[0], src[0], 0, 0]; }; self.mix1["1->6"] = function (src) { return [0, 0, src[0], 0, 0, 0]; }; self.mix1["2->4"] = function (src) { return [src[0], src[1], 0, 0]; }; self.mix1["2->6"] = function (src) { return [src[0], src[1], 0, 0, 0, 0]; }; self.mix1["4->6"] = function (src) { return [src[0], src[1], 0, 0, src[2], src[3]]; }; self.mix1["2->1"] = function (src) { return [0.5 * (src[0] + src[1])]; }; self.mix1["4->1"] = function (src) { return [0.25 * (src[0] + src[1] + src[2] + src[3])]; }; self.mix1["6->1"] = function (src) { return [0.7071 * (src[0] + src[1]) + src[2] + 0.5 * (src[4] + src[5])]; }; self.mix1["4->2"] = function (src) { return [0.5 * (src[0] + src[2]), 0.5 * (src[1] + src[3])]; }; self.mix1["6->2"] = function (src) { return [src[0] + 0.7071 * (src[2] + src[4]), src[1] + 0.7071 * (src[2] + src[5])]; }; self.mix1["6->4"] = function (src) { return [src[0] + 0.7071 * src[2], src[1] + 0.7071 * src[2], src[4], src[5]]; }; } render.self = render.util = self; module.exports = render;