Spaces:
Sleeping
Sleeping
//code taken from https://github.com/mattdiamond/Recorderjs/blob/master/src/recorder.js | |
(function (f) { if (typeof exports === "object" && typeof module !== "undefined") { module.exports = f() } else if (typeof define === "function" && define.amd) { define([], f) } else { var g; if (typeof window !== "undefined") { g = window } else if (typeof global !== "undefined") { g = global } else if (typeof self !== "undefined") { g = self } else { g = this } g.Recorder = f() } })(function () { | |
var define, module, exports; return (function e(t, n, r) { function s(o, u) { if (!n[o]) { if (!t[o]) { var a = typeof require == "function" && require; if (!u && a) return a(o, !0); if (i) return i(o, !0); var f = new Error("Cannot find module '" + o + "'"); throw f.code = "MODULE_NOT_FOUND", f } var l = n[o] = { exports: {} }; t[o][0].call(l.exports, function (e) { var n = t[o][1][e]; return s(n ? n : e) }, l, l.exports, e, t, n, r) } return n[o].exports } var i = typeof require == "function" && require; for (var o = 0; o < r.length; o++)s(r[o]); return s })({ | |
1: [function (require, module, exports) { | |
"use strict"; | |
module.exports = require("./recorder").Recorder; | |
}, { "./recorder": 2 }], 2: [function (require, module, exports) { | |
'use strict'; | |
var _createClass = (function () { | |
function defineProperties(target, props) { | |
for (var i = 0; i < props.length; i++) { | |
var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); | |
} | |
} return function (Constructor, protoProps, staticProps) { | |
if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; | |
}; | |
})(); | |
Object.defineProperty(exports, "__esModule", { | |
value: true | |
}); | |
exports.Recorder = undefined; | |
var _inlineWorker = require('inline-worker'); | |
var _inlineWorker2 = _interopRequireDefault(_inlineWorker); | |
function _interopRequireDefault(obj) { | |
return obj && obj.__esModule ? obj : { default: obj }; | |
} | |
function _classCallCheck(instance, Constructor) { | |
if (!(instance instanceof Constructor)) { | |
throw new TypeError("Cannot call a class as a function"); | |
} | |
} | |
var Recorder = exports.Recorder = (function () { | |
function Recorder(source, cfg) { | |
var _this = this; | |
_classCallCheck(this, Recorder); | |
this.config = { | |
bufferLen: 4096, | |
numChannels: 2, | |
mimeType: 'audio/wav' | |
}; | |
this.recording = false; | |
this.callbacks = { | |
getBuffer: [], | |
exportWAV: [] | |
}; | |
Object.assign(this.config, cfg); | |
this.context = source.context; | |
this.node = (this.context.createScriptProcessor || this.context.createJavaScriptNode).call(this.context, this.config.bufferLen, this.config.numChannels, this.config.numChannels); | |
this.node.onaudioprocess = function (e) { | |
if (!_this.recording) return; | |
var buffer = []; | |
for (var channel = 0; channel < _this.config.numChannels; channel++) { | |
buffer.push(e.inputBuffer.getChannelData(channel)); | |
} | |
_this.worker.postMessage({ | |
command: 'record', | |
buffer: buffer | |
}); | |
}; | |
source.connect(this.node); | |
this.node.connect(this.context.destination); //this should not be necessary | |
var self = {}; | |
this.worker = new _inlineWorker2.default(function () { | |
var recLength = 0, | |
recBuffers = [], | |
sampleRate = undefined, | |
numChannels = undefined; | |
self.onmessage = function (e) { | |
switch (e.data.command) { | |
case 'init': | |
init(e.data.config); | |
break; | |
case 'record': | |
record(e.data.buffer); | |
break; | |
case 'exportWAV': | |
exportWAV(e.data.type); | |
break; | |
case 'getBuffer': | |
getBuffer(); | |
break; | |
case 'clear': | |
clear(); | |
break; | |
} | |
}; | |
function init(config) { | |
sampleRate = config.sampleRate; | |
numChannels = config.numChannels; | |
initBuffers(); | |
} | |
function record(inputBuffer) { | |
for (var channel = 0; channel < numChannels; channel++) { | |
recBuffers[channel].push(inputBuffer[channel]); | |
} | |
recLength += inputBuffer[0].length; | |
} | |
function exportWAV(type) { | |
var buffers = []; | |
for (var channel = 0; channel < numChannels; channel++) { | |
buffers.push(mergeBuffers(recBuffers[channel], recLength)); | |
} | |
var interleaved = undefined; | |
if (numChannels === 2) { | |
interleaved = interleave(buffers[0], buffers[1]); | |
} else { | |
interleaved = buffers[0]; | |
} | |
var downsampledBuffer = downsampleBuffer(interleaved, 16000); | |
var dataview = encodeWAV(downsampledBuffer); | |
var audioBlob = new Blob([dataview], { type: type }); | |
self.postMessage({ command: 'exportWAV', data: audioBlob }); | |
} | |
function getBuffer() { | |
var buffers = []; | |
for (var channel = 0; channel < numChannels; channel++) { | |
buffers.push(mergeBuffers(recBuffers[channel], recLength)); | |
} | |
self.postMessage({ command: 'getBuffer', data: buffers }); | |
} | |
function clear() { | |
recLength = 0; | |
recBuffers = []; | |
initBuffers(); | |
} | |
function initBuffers() { | |
for (var channel = 0; channel < numChannels; channel++) { | |
recBuffers[channel] = []; | |
} | |
} | |
function mergeBuffers(recBuffers, recLength) { | |
var result = new Float32Array(recLength); | |
var offset = 0; | |
for (var i = 0; i < recBuffers.length; i++) { | |
result.set(recBuffers[i], offset); | |
offset += recBuffers[i].length; | |
} | |
return result; | |
} | |
function downsampleBuffer(buffer, rate) { | |
if (rate == sampleRate) { | |
return buffer; | |
} | |
if (rate > sampleRate) { | |
throw "downsampling rate show be smaller than original sample rate"; | |
} | |
var sampleRateRatio = sampleRate / rate; | |
var newLength = Math.round(buffer.length / sampleRateRatio); | |
var result = new Float32Array(newLength); | |
var offsetResult = 0; | |
var offsetBuffer = 0; | |
while (offsetResult < result.length) { | |
var nextOffsetBuffer = Math.round((offsetResult + 1) * sampleRateRatio); | |
// Use average value of skipped samples | |
var accum = 0, count = 0; | |
for (var i = offsetBuffer; i < nextOffsetBuffer && i < buffer.length; i++) { | |
accum += buffer[i]; | |
count++; | |
} | |
result[offsetResult] = accum / count; | |
// Or you can simply get rid of the skipped samples: | |
// result[offsetResult] = buffer[nextOffsetBuffer]; | |
offsetResult++; | |
offsetBuffer = nextOffsetBuffer; | |
} | |
return result; | |
} | |
function interleave(inputL, inputR) { | |
var length = inputL.length + inputR.length; | |
var result = new Float32Array(length); | |
var index = 0, | |
inputIndex = 0; | |
while (index < length) { | |
result[index++] = inputL[inputIndex]; | |
result[index++] = inputR[inputIndex]; | |
inputIndex++; | |
} | |
return result; | |
} | |
function floatTo16BitPCM(output, offset, input) { | |
for (var i = 0; i < input.length; i++, offset += 2) { | |
var s = Math.max(-1, Math.min(1, input[i])); | |
output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true); | |
} | |
} | |
function writeString(view, offset, string) { | |
for (var i = 0; i < string.length; i++) { | |
view.setUint8(offset + i, string.charCodeAt(i)); | |
} | |
} | |
function encodeWAV(samples) { | |
var buffer = new ArrayBuffer(44 + samples.length * 2); | |
var view = new DataView(buffer); | |
/* RIFF identifier */ | |
writeString(view, 0, 'RIFF'); | |
/* RIFF chunk length */ | |
view.setUint32(4, 36 + samples.length * 2, true); | |
/* RIFF type */ | |
writeString(view, 8, 'WAVE'); | |
/* format chunk identifier */ | |
writeString(view, 12, 'fmt '); | |
/* format chunk length */ | |
view.setUint32(16, 16, true); | |
/* sample format (raw) */ | |
view.setUint16(20, 1, true); | |
/* channel count */ | |
view.setUint16(22, numChannels, true); | |
/* sample rate */ | |
view.setUint32(24, 16000, true); | |
/* byte rate (sample rate * block align) */ | |
view.setUint32(28, 16000 * 4, true); | |
/* sample rate */ | |
//view.setUint32(24, sampleRate, true); | |
/* byte rate (sample rate * block align) */ | |
//view.setUint32(28, sampleRate * 4, true); | |
/* block align (channel count * bytes per sample) */ | |
view.setUint16(32, numChannels * 2, true); | |
/* bits per sample */ | |
view.setUint16(34, 16, true); | |
/* data chunk identifier */ | |
writeString(view, 36, 'data'); | |
/* data chunk length */ | |
view.setUint32(40, samples.length * 2, true); | |
floatTo16BitPCM(view, 44, samples); | |
return view; | |
} | |
}, self); | |
this.worker.postMessage({ | |
command: 'init', | |
config: { | |
sampleRate: this.context.sampleRate, | |
numChannels: this.config.numChannels | |
} | |
}); | |
this.worker.onmessage = function (e) { | |
var cb = _this.callbacks[e.data.command].pop(); | |
if (typeof cb == 'function') { | |
cb(e.data.data); | |
} | |
}; | |
} | |
_createClass(Recorder, [{ | |
key: 'record', | |
value: function record() { | |
this.recording = true; | |
} | |
}, { | |
key: 'stop', | |
value: function stop() { | |
this.recording = false; | |
} | |
}, { | |
key: 'clear', | |
value: function clear() { | |
this.worker.postMessage({ command: 'clear' }); | |
} | |
}, { | |
key: 'getBuffer', | |
value: function getBuffer(cb) { | |
cb = cb || this.config.callback; | |
if (!cb) throw new Error('Callback not set'); | |
this.callbacks.getBuffer.push(cb); | |
this.worker.postMessage({ command: 'getBuffer' }); | |
} | |
}, { | |
key: 'exportWAV', | |
value: function exportWAV(cb, mimeType) { | |
mimeType = mimeType || this.config.mimeType; | |
cb = cb || this.config.callback; | |
if (!cb) throw new Error('Callback not set'); | |
this.callbacks.exportWAV.push(cb); | |
this.worker.postMessage({ | |
command: 'exportWAV', | |
type: mimeType | |
}); | |
} | |
}], [{ | |
key: 'forceDownload', | |
value: function forceDownload(blob, filename) { | |
var url = (window.URL || window.webkitURL).createObjectURL(blob); | |
var link = window.document.createElement('a'); | |
link.href = url; | |
link.download = filename || 'output.wav'; | |
var click = document.createEvent("Event"); | |
click.initEvent("click", true, true); | |
link.dispatchEvent(click); | |
} | |
}]); | |
return Recorder; | |
})(); | |
exports.default = Recorder; | |
}, { "inline-worker": 3 }], 3: [function (require, module, exports) { | |
"use strict"; | |
module.exports = require("./inline-worker"); | |
}, { "./inline-worker": 4 }], 4: [function (require, module, exports) { | |
(function (global) { | |
"use strict"; | |
var _createClass = (function () { function defineProperties(target, props) { for (var key in props) { var prop = props[key]; prop.configurable = true; if (prop.value) prop.writable = true; } Object.defineProperties(target, props); } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); | |
var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; | |
var WORKER_ENABLED = !!(global === global.window && global.URL && global.Blob && global.Worker); | |
var InlineWorker = (function () { | |
function InlineWorker(func, self) { | |
var _this = this; | |
_classCallCheck(this, InlineWorker); | |
if (WORKER_ENABLED) { | |
var functionBody = func.toString().trim().match(/^function\s*\w*\s*\([\w\s,]*\)\s*{([\w\W]*?)}$/)[1]; | |
var url = global.URL.createObjectURL(new global.Blob([functionBody], { type: "text/javascript" })); | |
return new global.Worker(url); | |
} | |
this.self = self; | |
this.self.postMessage = function (data) { | |
setTimeout(function () { | |
_this.onmessage({ data: data }); | |
}, 0); | |
}; | |
setTimeout(function () { | |
func.call(self); | |
}, 0); | |
} | |
_createClass(InlineWorker, { | |
postMessage: { | |
value: function postMessage(data) { | |
var _this = this; | |
setTimeout(function () { | |
_this.self.onmessage({ data: data }); | |
}, 0); | |
} | |
} | |
}); | |
return InlineWorker; | |
})(); | |
module.exports = InlineWorker; | |
}).call(this, typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
}, {}] | |
}, {}, [1])(1) | |
}); |