remove mp3 lame already file and webrecorder worker unused
parent
cf71c9a00f
commit
2bcc7be3e6
@ -1,292 +0,0 @@
|
|||||||
/*!
|
|
||||||
Autosize 4.0.0
|
|
||||||
license: MIT
|
|
||||||
http://www.jacklmoore.com/autosize
|
|
||||||
*/
|
|
||||||
(function (global, factory) {
|
|
||||||
if (typeof define === 'function' && define.amd) {
|
|
||||||
define(['exports', 'module'], factory);
|
|
||||||
} else if (typeof exports !== 'undefined' && typeof module !== 'undefined') {
|
|
||||||
factory(exports, module);
|
|
||||||
} else {
|
|
||||||
var mod = {
|
|
||||||
exports: {}
|
|
||||||
};
|
|
||||||
factory(mod.exports, mod);
|
|
||||||
global.autosize = mod.exports;
|
|
||||||
}
|
|
||||||
})(this, function (exports, module) {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var map = typeof Map === "function" ? new Map() : (function () {
|
|
||||||
var keys = [];
|
|
||||||
var values = [];
|
|
||||||
|
|
||||||
return {
|
|
||||||
has: function has(key) {
|
|
||||||
return keys.indexOf(key) > -1;
|
|
||||||
},
|
|
||||||
get: function get(key) {
|
|
||||||
return values[keys.indexOf(key)];
|
|
||||||
},
|
|
||||||
set: function set(key, value) {
|
|
||||||
if (keys.indexOf(key) === -1) {
|
|
||||||
keys.push(key);
|
|
||||||
values.push(value);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'delete': function _delete(key) {
|
|
||||||
var index = keys.indexOf(key);
|
|
||||||
if (index > -1) {
|
|
||||||
keys.splice(index, 1);
|
|
||||||
values.splice(index, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
|
|
||||||
var createEvent = function createEvent(name) {
|
|
||||||
return new Event(name, { bubbles: true });
|
|
||||||
};
|
|
||||||
try {
|
|
||||||
new Event('test');
|
|
||||||
} catch (e) {
|
|
||||||
// IE does not support `new Event()`
|
|
||||||
createEvent = function (name) {
|
|
||||||
var evt = document.createEvent('Event');
|
|
||||||
evt.initEvent(name, true, false);
|
|
||||||
return evt;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function assign(ta) {
|
|
||||||
if (!ta || !ta.nodeName || ta.nodeName !== 'TEXTAREA' || map.has(ta)) return;
|
|
||||||
|
|
||||||
var heightOffset = null;
|
|
||||||
var clientWidth = ta.clientWidth;
|
|
||||||
var cachedHeight = null;
|
|
||||||
|
|
||||||
function init() {
|
|
||||||
var style = window.getComputedStyle(ta, null);
|
|
||||||
|
|
||||||
if (style.resize === 'vertical') {
|
|
||||||
ta.style.resize = 'none';
|
|
||||||
} else if (style.resize === 'both') {
|
|
||||||
ta.style.resize = 'horizontal';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (style.boxSizing === 'content-box') {
|
|
||||||
heightOffset = -(parseFloat(style.paddingTop) + parseFloat(style.paddingBottom));
|
|
||||||
} else {
|
|
||||||
heightOffset = parseFloat(style.borderTopWidth) + parseFloat(style.borderBottomWidth);
|
|
||||||
}
|
|
||||||
// Fix when a textarea is not on document body and heightOffset is Not a Number
|
|
||||||
if (isNaN(heightOffset)) {
|
|
||||||
heightOffset = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
function changeOverflow(value) {
|
|
||||||
{
|
|
||||||
// Chrome/Safari-specific fix:
|
|
||||||
// When the textarea y-overflow is hidden, Chrome/Safari do not reflow the text to account for the space
|
|
||||||
// made available by removing the scrollbar. The following forces the necessary text reflow.
|
|
||||||
var width = ta.style.width;
|
|
||||||
ta.style.width = '0px';
|
|
||||||
// Force reflow:
|
|
||||||
/* jshint ignore:start */
|
|
||||||
ta.offsetWidth;
|
|
||||||
/* jshint ignore:end */
|
|
||||||
ta.style.width = width;
|
|
||||||
}
|
|
||||||
|
|
||||||
ta.style.overflowY = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getParentOverflows(el) {
|
|
||||||
var arr = [];
|
|
||||||
|
|
||||||
while (el && el.parentNode && el.parentNode instanceof Element) {
|
|
||||||
if (el.parentNode.scrollTop) {
|
|
||||||
arr.push({
|
|
||||||
node: el.parentNode,
|
|
||||||
scrollTop: el.parentNode.scrollTop
|
|
||||||
});
|
|
||||||
}
|
|
||||||
el = el.parentNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
return arr;
|
|
||||||
}
|
|
||||||
|
|
||||||
function resize() {
|
|
||||||
var originalHeight = ta.style.height;
|
|
||||||
var overflows = getParentOverflows(ta);
|
|
||||||
var docTop = document.documentElement && document.documentElement.scrollTop; // Needed for Mobile IE (ticket #240)
|
|
||||||
|
|
||||||
ta.style.height = '';
|
|
||||||
|
|
||||||
var endHeight = ta.scrollHeight + heightOffset;
|
|
||||||
|
|
||||||
if (ta.scrollHeight === 0) {
|
|
||||||
// If the scrollHeight is 0, then the element probably has display:none or is detached from the DOM.
|
|
||||||
ta.style.height = originalHeight;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ta.style.height = endHeight + 'px';
|
|
||||||
|
|
||||||
// used to check if an update is actually necessary on window.resize
|
|
||||||
clientWidth = ta.clientWidth;
|
|
||||||
|
|
||||||
// prevents scroll-position jumping
|
|
||||||
overflows.forEach(function (el) {
|
|
||||||
el.node.scrollTop = el.scrollTop;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (docTop) {
|
|
||||||
document.documentElement.scrollTop = docTop;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function update() {
|
|
||||||
resize();
|
|
||||||
|
|
||||||
var styleHeight = Math.round(parseFloat(ta.style.height));
|
|
||||||
var computed = window.getComputedStyle(ta, null);
|
|
||||||
|
|
||||||
// Using offsetHeight as a replacement for computed.height in IE, because IE does not account use of border-box
|
|
||||||
var actualHeight = computed.boxSizing === 'content-box' ? Math.round(parseFloat(computed.height)) : ta.offsetHeight;
|
|
||||||
|
|
||||||
// The actual height not matching the style height (set via the resize method) indicates that
|
|
||||||
// the max-height has been exceeded, in which case the overflow should be allowed.
|
|
||||||
if (actualHeight !== styleHeight) {
|
|
||||||
if (computed.overflowY === 'hidden') {
|
|
||||||
changeOverflow('scroll');
|
|
||||||
resize();
|
|
||||||
actualHeight = computed.boxSizing === 'content-box' ? Math.round(parseFloat(window.getComputedStyle(ta, null).height)) : ta.offsetHeight;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Normally keep overflow set to hidden, to avoid flash of scrollbar as the textarea expands.
|
|
||||||
if (computed.overflowY !== 'hidden') {
|
|
||||||
changeOverflow('hidden');
|
|
||||||
resize();
|
|
||||||
actualHeight = computed.boxSizing === 'content-box' ? Math.round(parseFloat(window.getComputedStyle(ta, null).height)) : ta.offsetHeight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cachedHeight !== actualHeight) {
|
|
||||||
cachedHeight = actualHeight;
|
|
||||||
var evt = createEvent('autosize:resized');
|
|
||||||
try {
|
|
||||||
ta.dispatchEvent(evt);
|
|
||||||
} catch (err) {
|
|
||||||
// Firefox will throw an error on dispatchEvent for a detached element
|
|
||||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=889376
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var pageResize = function pageResize() {
|
|
||||||
if (ta.clientWidth !== clientWidth) {
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var destroy = (function (style) {
|
|
||||||
window.removeEventListener('resize', pageResize, false);
|
|
||||||
ta.removeEventListener('input', update, false);
|
|
||||||
ta.removeEventListener('keyup', update, false);
|
|
||||||
ta.removeEventListener('autosize:destroy', destroy, false);
|
|
||||||
ta.removeEventListener('autosize:update', update, false);
|
|
||||||
|
|
||||||
Object.keys(style).forEach(function (key) {
|
|
||||||
ta.style[key] = style[key];
|
|
||||||
});
|
|
||||||
|
|
||||||
map['delete'](ta);
|
|
||||||
}).bind(ta, {
|
|
||||||
height: ta.style.height,
|
|
||||||
resize: ta.style.resize,
|
|
||||||
overflowY: ta.style.overflowY,
|
|
||||||
overflowX: ta.style.overflowX,
|
|
||||||
wordWrap: ta.style.wordWrap
|
|
||||||
});
|
|
||||||
|
|
||||||
ta.addEventListener('autosize:destroy', destroy, false);
|
|
||||||
|
|
||||||
// IE9 does not fire onpropertychange or oninput for deletions,
|
|
||||||
// so binding to onkeyup to catch most of those events.
|
|
||||||
// There is no way that I know of to detect something like 'cut' in IE9.
|
|
||||||
if ('onpropertychange' in ta && 'oninput' in ta) {
|
|
||||||
ta.addEventListener('keyup', update, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener('resize', pageResize, false);
|
|
||||||
ta.addEventListener('input', update, false);
|
|
||||||
ta.addEventListener('autosize:update', update, false);
|
|
||||||
ta.style.overflowX = 'hidden';
|
|
||||||
ta.style.wordWrap = 'break-word';
|
|
||||||
|
|
||||||
map.set(ta, {
|
|
||||||
destroy: destroy,
|
|
||||||
update: update
|
|
||||||
});
|
|
||||||
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
function destroy(ta) {
|
|
||||||
var methods = map.get(ta);
|
|
||||||
if (methods) {
|
|
||||||
methods.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function update(ta) {
|
|
||||||
var methods = map.get(ta);
|
|
||||||
if (methods) {
|
|
||||||
methods.update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var autosize = null;
|
|
||||||
|
|
||||||
// Do nothing in Node.js environment and IE8 (or lower)
|
|
||||||
if (typeof window === 'undefined' || typeof window.getComputedStyle !== 'function') {
|
|
||||||
autosize = function (el) {
|
|
||||||
return el;
|
|
||||||
};
|
|
||||||
autosize.destroy = function (el) {
|
|
||||||
return el;
|
|
||||||
};
|
|
||||||
autosize.update = function (el) {
|
|
||||||
return el;
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
autosize = function (el, options) {
|
|
||||||
if (el) {
|
|
||||||
Array.prototype.forEach.call(el.length ? el : [el], function (x) {
|
|
||||||
return assign(x, options);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return el;
|
|
||||||
};
|
|
||||||
autosize.destroy = function (el) {
|
|
||||||
if (el) {
|
|
||||||
Array.prototype.forEach.call(el.length ? el : [el], destroy);
|
|
||||||
}
|
|
||||||
return el;
|
|
||||||
};
|
|
||||||
autosize.update = function (el) {
|
|
||||||
if (el) {
|
|
||||||
Array.prototype.forEach.call(el.length ? el : [el], update);
|
|
||||||
}
|
|
||||||
return el;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = autosize;
|
|
||||||
});
|
|
File diff suppressed because one or more lines are too long
@ -1,97 +0,0 @@
|
|||||||
(function(window){
|
|
||||||
|
|
||||||
var WORKER_PATH = 'recorderWorker.js';
|
|
||||||
|
|
||||||
var Recorder = function(source, cfg){
|
|
||||||
var config = cfg || {};
|
|
||||||
var bufferLen = config.bufferLen || 4096;
|
|
||||||
this.context = source.context;
|
|
||||||
this.node = (this.context.createScriptProcessor ||
|
|
||||||
this.context.createJavaScriptNode).call(this.context,
|
|
||||||
bufferLen, 2, 2);
|
|
||||||
var worker = new Worker(config.workerPath || WORKER_PATH);
|
|
||||||
worker.postMessage({
|
|
||||||
command: 'init',
|
|
||||||
config: {
|
|
||||||
sampleRate: this.context.sampleRate
|
|
||||||
}
|
|
||||||
});
|
|
||||||
var recording = false,
|
|
||||||
currCallback;
|
|
||||||
|
|
||||||
var self = this;
|
|
||||||
this.node.onaudioprocess = function(e){
|
|
||||||
if (!recording) return;
|
|
||||||
self.ondata && self.ondata(e.inputBuffer.getChannelData(0));
|
|
||||||
worker.postMessage({
|
|
||||||
command: 'record',
|
|
||||||
buffer: [
|
|
||||||
e.inputBuffer.getChannelData(0),
|
|
||||||
e.inputBuffer.getChannelData(1)
|
|
||||||
]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.configure = function(cfg){
|
|
||||||
for (var prop in cfg){
|
|
||||||
if (cfg.hasOwnProperty(prop)){
|
|
||||||
config[prop] = cfg[prop];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.record = function(){
|
|
||||||
recording = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.stop = function(){
|
|
||||||
recording = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.clear = function(){
|
|
||||||
worker.postMessage({ command: 'clear' });
|
|
||||||
}
|
|
||||||
|
|
||||||
this.getBuffer = function(cb) {
|
|
||||||
currCallback = cb || config.callback;
|
|
||||||
worker.postMessage({ command: 'getBuffer' })
|
|
||||||
}
|
|
||||||
|
|
||||||
this.exportWAV = function(cb, type){
|
|
||||||
currCallback = cb || config.callback;
|
|
||||||
type = type || config.type || 'audio/wav';
|
|
||||||
if (!currCallback) throw new Error('Callback not set');
|
|
||||||
worker.postMessage({
|
|
||||||
command: 'exportWAV',
|
|
||||||
type: type
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.shutdown = function(){
|
|
||||||
worker.terminate();
|
|
||||||
source.disconnect();
|
|
||||||
this.node.disconnect();
|
|
||||||
};
|
|
||||||
|
|
||||||
worker.onmessage = function(e){
|
|
||||||
var blob = e.data;
|
|
||||||
currCallback(blob);
|
|
||||||
}
|
|
||||||
|
|
||||||
source.connect(this.node);
|
|
||||||
this.node.connect(this.context.destination); //this should not be necessary
|
|
||||||
};
|
|
||||||
|
|
||||||
Recorder.forceDownload = function(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);
|
|
||||||
}
|
|
||||||
|
|
||||||
window.Recorder = Recorder;
|
|
||||||
|
|
||||||
})(window);
|
|
@ -1,131 +0,0 @@
|
|||||||
var recLength = 0,
|
|
||||||
recBuffersL = [],
|
|
||||||
recBuffersR = [],
|
|
||||||
sampleRate;
|
|
||||||
|
|
||||||
this.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
function record(inputBuffer){
|
|
||||||
recBuffersL.push(inputBuffer[0]);
|
|
||||||
recBuffersR.push(inputBuffer[1]);
|
|
||||||
recLength += inputBuffer[0].length;
|
|
||||||
}
|
|
||||||
|
|
||||||
function exportWAV(type){
|
|
||||||
var bufferL = mergeBuffers(recBuffersL, recLength);
|
|
||||||
var bufferR = mergeBuffers(recBuffersR, recLength);
|
|
||||||
var interleaved = interleave(bufferL, bufferR);
|
|
||||||
var dataview = encodeWAV(interleaved);
|
|
||||||
var audioBlob = new Blob([dataview], { type: type });
|
|
||||||
|
|
||||||
this.postMessage(audioBlob);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getBuffer() {
|
|
||||||
var buffers = [];
|
|
||||||
buffers.push( mergeBuffers(recBuffersL, recLength) );
|
|
||||||
buffers.push( mergeBuffers(recBuffersR, recLength) );
|
|
||||||
this.postMessage(buffers);
|
|
||||||
}
|
|
||||||
|
|
||||||
function clear(){
|
|
||||||
recLength = 0;
|
|
||||||
recBuffersL = [];
|
|
||||||
recBuffersR = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
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 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, 2, 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, 4, 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;
|
|
||||||
}
|
|
@ -1,199 +0,0 @@
|
|||||||
(function(window) {
|
|
||||||
// internal: same as jQuery.extend(true, args...)
|
|
||||||
var extend = function() {
|
|
||||||
var target = arguments[0],
|
|
||||||
sources = [].slice.call(arguments, 1);
|
|
||||||
for (var i = 0; i < sources.length; ++i) {
|
|
||||||
var src = sources[i];
|
|
||||||
for (key in src) {
|
|
||||||
var val = src[key];
|
|
||||||
target[key] = typeof val === "object"
|
|
||||||
? extend(typeof target[key] === "object" ? target[key] : {}, val)
|
|
||||||
: val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return target;
|
|
||||||
};
|
|
||||||
|
|
||||||
var WORKER_FILE = {
|
|
||||||
wav: "WebAudioRecorderWav.js",
|
|
||||||
ogg: "WebAudioRecorderOgg.js",
|
|
||||||
mp3: "WebAudioRecorderMp3.js"
|
|
||||||
};
|
|
||||||
|
|
||||||
// default configs
|
|
||||||
var CONFIGS = {
|
|
||||||
workerDir: "/", // worker scripts dir (end with /)
|
|
||||||
numChannels: 2, // number of channels
|
|
||||||
encoding: "wav", // encoding (can be changed at runtime)
|
|
||||||
|
|
||||||
// runtime options
|
|
||||||
options: {
|
|
||||||
timeLimit: 300, // recording time limit (sec)
|
|
||||||
encodeAfterRecord: false, // process encoding after recording
|
|
||||||
progressInterval: 1000, // encoding progress report interval (millisec)
|
|
||||||
bufferSize: undefined, // buffer size (use browser default)
|
|
||||||
|
|
||||||
// encoding-specific options
|
|
||||||
wav: {
|
|
||||||
mimeType: "audio/wav"
|
|
||||||
},
|
|
||||||
ogg: {
|
|
||||||
mimeType: "audio/ogg",
|
|
||||||
quality: 0.5 // (VBR only): quality = [-0.1 .. 1]
|
|
||||||
},
|
|
||||||
mp3: {
|
|
||||||
mimeType: "audio/mpeg",
|
|
||||||
bitRate: 160 // (CBR only): bit rate = [64 .. 320]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// constructor
|
|
||||||
var WebAudioRecorder = function(sourceNode, configs) {
|
|
||||||
extend(this, CONFIGS, configs || {});
|
|
||||||
this.context = sourceNode.context;
|
|
||||||
if (this.context.createScriptProcessor == null)
|
|
||||||
this.context.createScriptProcessor = this.context.createJavaScriptNode;
|
|
||||||
this.input = this.context.createGain();
|
|
||||||
sourceNode.connect(this.input);
|
|
||||||
this.buffer = [];
|
|
||||||
this.initWorker();
|
|
||||||
};
|
|
||||||
|
|
||||||
// instance methods
|
|
||||||
extend(WebAudioRecorder.prototype, {
|
|
||||||
isRecording: function() { return this.processor != null; },
|
|
||||||
|
|
||||||
setEncoding: function(encoding) {
|
|
||||||
if (this.isRecording())
|
|
||||||
this.error("setEncoding: cannot set encoding during recording");
|
|
||||||
else if (this.encoding !== encoding) {
|
|
||||||
this.encoding = encoding;
|
|
||||||
this.initWorker();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
setOptions: function(options) {
|
|
||||||
if (this.isRecording())
|
|
||||||
this.error("setOptions: cannot set options during recording");
|
|
||||||
else {
|
|
||||||
extend(this.options, options);
|
|
||||||
this.worker.postMessage({ command: "options", options: this.options });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
startRecording: function() {
|
|
||||||
if (this.isRecording())
|
|
||||||
this.error("startRecording: previous recording is running");
|
|
||||||
else {
|
|
||||||
var numChannels = this.numChannels,
|
|
||||||
buffer = this.buffer,
|
|
||||||
worker = this.worker;
|
|
||||||
this.processor = this.context.createScriptProcessor(
|
|
||||||
this.options.bufferSize,
|
|
||||||
this.numChannels, this.numChannels);
|
|
||||||
this.input.connect(this.processor);
|
|
||||||
this.processor.connect(this.context.destination);
|
|
||||||
this.processor.onaudioprocess = function(event) {
|
|
||||||
for (var ch = 0; ch < numChannels; ++ch)
|
|
||||||
buffer[ch] = event.inputBuffer.getChannelData(ch);
|
|
||||||
worker.postMessage({ command: "record", buffer: buffer });
|
|
||||||
};
|
|
||||||
this.worker.postMessage({
|
|
||||||
command: "start",
|
|
||||||
bufferSize: this.processor.bufferSize
|
|
||||||
});
|
|
||||||
this.startTime = Date.now();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
recordingTime: function() {
|
|
||||||
return this.isRecording() ? (Date.now() - this.startTime) * 0.001 : null;
|
|
||||||
},
|
|
||||||
|
|
||||||
cancelRecording: function() {
|
|
||||||
if (this.isRecording()) {
|
|
||||||
this.input.disconnect();
|
|
||||||
this.processor.disconnect();
|
|
||||||
delete this.processor;
|
|
||||||
this.worker.postMessage({ command: "cancel" });
|
|
||||||
} else
|
|
||||||
this.error("cancelRecording: no recording is running");
|
|
||||||
},
|
|
||||||
|
|
||||||
finishRecording: function() {
|
|
||||||
if (this.isRecording()) {
|
|
||||||
this.input.disconnect();
|
|
||||||
this.processor.disconnect();
|
|
||||||
delete this.processor;
|
|
||||||
this.worker.postMessage({ command: "finish" });
|
|
||||||
} else
|
|
||||||
this.error("finishRecording: no recording is running");
|
|
||||||
},
|
|
||||||
|
|
||||||
cancelEncoding: function() {
|
|
||||||
if (this.options.encodeAfterRecord)
|
|
||||||
if (this.isRecording())
|
|
||||||
this.error("cancelEncoding: recording is not finished");
|
|
||||||
else {
|
|
||||||
this.onEncodingCanceled(this);
|
|
||||||
this.initWorker();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
this.error("cancelEncoding: invalid method call");
|
|
||||||
},
|
|
||||||
|
|
||||||
initWorker: function() {
|
|
||||||
if (this.worker != null)
|
|
||||||
this.worker.terminate();
|
|
||||||
this.onEncoderLoading(this, this.encoding);
|
|
||||||
this.worker = new Worker(this.workerDir + WORKER_FILE[this.encoding]);
|
|
||||||
var _this = this;
|
|
||||||
this.worker.onmessage = function(event) {
|
|
||||||
var data = event.data;
|
|
||||||
switch (data.command) {
|
|
||||||
case "loaded":
|
|
||||||
_this.onEncoderLoaded(_this, _this.encoding);
|
|
||||||
break;
|
|
||||||
case "timeout":
|
|
||||||
_this.onTimeout(_this);
|
|
||||||
break;
|
|
||||||
case "progress":
|
|
||||||
_this.onEncodingProgress(_this, data.progress);
|
|
||||||
break;
|
|
||||||
case "complete":
|
|
||||||
_this.onComplete(_this, data.blob);
|
|
||||||
break;
|
|
||||||
case "error":
|
|
||||||
_this.error(data.message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this.worker.postMessage({
|
|
||||||
command: "init",
|
|
||||||
config: {
|
|
||||||
sampleRate: this.context.sampleRate,
|
|
||||||
numChannels: this.numChannels
|
|
||||||
},
|
|
||||||
options: this.options
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
error: function(message) {
|
|
||||||
this.onError(this, "WebAudioRecorder.js:" + message);
|
|
||||||
},
|
|
||||||
|
|
||||||
// event handlers
|
|
||||||
onEncoderLoading: function(recorder, encoding) {},
|
|
||||||
onEncoderLoaded: function(recorder, encoding) {},
|
|
||||||
onTimeout: function(recorder) { recorder.finishRecording(); },
|
|
||||||
onEncodingProgress: function (recorder, progress) {},
|
|
||||||
onEncodingCanceled: function(recorder) {},
|
|
||||||
onComplete: function(recorder, blob) {
|
|
||||||
recorder.onError(recorder, "WebAudioRecorder.js: You must override .onComplete event");
|
|
||||||
},
|
|
||||||
onError: function(recorder, message) { console.log(message); }
|
|
||||||
});
|
|
||||||
|
|
||||||
window.WebAudioRecorder = WebAudioRecorder;
|
|
||||||
})(window);
|
|
@ -1,91 +0,0 @@
|
|||||||
importScripts("Mp3LameEncoder.min.js");
|
|
||||||
|
|
||||||
var NUM_CH = 2, // constant
|
|
||||||
sampleRate = 44100,
|
|
||||||
options = undefined,
|
|
||||||
maxBuffers = undefined,
|
|
||||||
encoder = undefined,
|
|
||||||
recBuffers = undefined,
|
|
||||||
bufferCount = 0;
|
|
||||||
|
|
||||||
function error(message) {
|
|
||||||
self.postMessage({ command: "error", message: "mp3: " + message });
|
|
||||||
}
|
|
||||||
|
|
||||||
function init(data) {
|
|
||||||
if (data.config.numChannels === NUM_CH) {
|
|
||||||
sampleRate = data.config.sampleRate;
|
|
||||||
options = data.options;
|
|
||||||
} else
|
|
||||||
error("numChannels must be " + NUM_CH);
|
|
||||||
};
|
|
||||||
|
|
||||||
function setOptions(opt) {
|
|
||||||
if (encoder || recBuffers)
|
|
||||||
error("cannot set options during recording");
|
|
||||||
else
|
|
||||||
options = opt;
|
|
||||||
}
|
|
||||||
|
|
||||||
function start(bufferSize) {
|
|
||||||
maxBuffers = Math.ceil(options.timeLimit * sampleRate / bufferSize);
|
|
||||||
if (options.encodeAfterRecord)
|
|
||||||
recBuffers = [];
|
|
||||||
else
|
|
||||||
encoder = new Mp3LameEncoder(sampleRate, options.mp3.bitRate);
|
|
||||||
}
|
|
||||||
|
|
||||||
function record(buffer) {
|
|
||||||
if (bufferCount++ < maxBuffers)
|
|
||||||
if (encoder)
|
|
||||||
encoder.encode(buffer);
|
|
||||||
else
|
|
||||||
recBuffers.push(buffer);
|
|
||||||
else
|
|
||||||
self.postMessage({ command: "timeout" });
|
|
||||||
};
|
|
||||||
|
|
||||||
function postProgress(progress) {
|
|
||||||
self.postMessage({ command: "progress", progress: progress });
|
|
||||||
};
|
|
||||||
|
|
||||||
function finish() {
|
|
||||||
if (recBuffers) {
|
|
||||||
postProgress(0);
|
|
||||||
encoder = new Mp3LameEncoder(sampleRate, options.mp3.bitRate);
|
|
||||||
var timeout = Date.now() + options.progressInterval;
|
|
||||||
while (recBuffers.length > 0) {
|
|
||||||
encoder.encode(recBuffers.shift());
|
|
||||||
var now = Date.now();
|
|
||||||
if (now > timeout) {
|
|
||||||
postProgress((bufferCount - recBuffers.length) / bufferCount);
|
|
||||||
timeout = now + options.progressInterval;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
postProgress(1);
|
|
||||||
}
|
|
||||||
self.postMessage({
|
|
||||||
command: "complete",
|
|
||||||
blob: encoder.finish(options.mp3.mimeType)
|
|
||||||
});
|
|
||||||
cleanup();
|
|
||||||
};
|
|
||||||
|
|
||||||
function cleanup() {
|
|
||||||
encoder = recBuffers = undefined;
|
|
||||||
bufferCount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.onmessage = function(event) {
|
|
||||||
var data = event.data;
|
|
||||||
switch (data.command) {
|
|
||||||
case "init": init(data); break;
|
|
||||||
case "options": setOptions(data.options); break;
|
|
||||||
case "start": start(data.bufferSize); break;
|
|
||||||
case "record": record(data.buffer); break;
|
|
||||||
case "finish": finish(); break;
|
|
||||||
case "cancel": cleanup();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
self.postMessage({ command: "loaded" });
|
|
File diff suppressed because one or more lines are too long
@ -1,91 +0,0 @@
|
|||||||
importScripts("Mp3LameEncoder.min.js");
|
|
||||||
|
|
||||||
var NUM_CH = 2, // constant
|
|
||||||
sampleRate = 44100,
|
|
||||||
options = undefined,
|
|
||||||
maxBuffers = undefined,
|
|
||||||
encoder = undefined,
|
|
||||||
recBuffers = undefined,
|
|
||||||
bufferCount = 0;
|
|
||||||
|
|
||||||
function error(message) {
|
|
||||||
self.postMessage({ command: "error", message: "mp3: " + message });
|
|
||||||
}
|
|
||||||
|
|
||||||
function init(data) {
|
|
||||||
if (data.config.numChannels === NUM_CH) {
|
|
||||||
sampleRate = data.config.sampleRate;
|
|
||||||
options = data.options;
|
|
||||||
} else
|
|
||||||
error("numChannels must be " + NUM_CH);
|
|
||||||
};
|
|
||||||
|
|
||||||
function setOptions(opt) {
|
|
||||||
if (encoder || recBuffers)
|
|
||||||
error("cannot set options during recording");
|
|
||||||
else
|
|
||||||
options = opt;
|
|
||||||
}
|
|
||||||
|
|
||||||
function start(bufferSize) {
|
|
||||||
maxBuffers = Math.ceil(options.timeLimit * sampleRate / bufferSize);
|
|
||||||
if (options.encodeAfterRecord)
|
|
||||||
recBuffers = [];
|
|
||||||
else
|
|
||||||
encoder = new Mp3LameEncoder(sampleRate, options.mp3.bitRate);
|
|
||||||
}
|
|
||||||
|
|
||||||
function record(buffer) {
|
|
||||||
if (bufferCount++ < maxBuffers)
|
|
||||||
if (encoder)
|
|
||||||
encoder.encode(buffer);
|
|
||||||
else
|
|
||||||
recBuffers.push(buffer);
|
|
||||||
else
|
|
||||||
self.postMessage({ command: "timeout" });
|
|
||||||
};
|
|
||||||
|
|
||||||
function postProgress(progress) {
|
|
||||||
self.postMessage({ command: "progress", progress: progress });
|
|
||||||
};
|
|
||||||
|
|
||||||
function finish() {
|
|
||||||
if (recBuffers) {
|
|
||||||
postProgress(0);
|
|
||||||
encoder = new Mp3LameEncoder(sampleRate, options.mp3.bitRate);
|
|
||||||
var timeout = Date.now() + options.progressInterval;
|
|
||||||
while (recBuffers.length > 0) {
|
|
||||||
encoder.encode(recBuffers.shift());
|
|
||||||
var now = Date.now();
|
|
||||||
if (now > timeout) {
|
|
||||||
postProgress((bufferCount - recBuffers.length) / bufferCount);
|
|
||||||
timeout = now + options.progressInterval;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
postProgress(1);
|
|
||||||
}
|
|
||||||
self.postMessage({
|
|
||||||
command: "complete",
|
|
||||||
blob: encoder.finish(options.mp3.mimeType)
|
|
||||||
});
|
|
||||||
cleanup();
|
|
||||||
};
|
|
||||||
|
|
||||||
function cleanup() {
|
|
||||||
encoder = recBuffers = undefined;
|
|
||||||
bufferCount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.onmessage = function(event) {
|
|
||||||
var data = event.data;
|
|
||||||
switch (data.command) {
|
|
||||||
case "init": init(data); break;
|
|
||||||
case "options": setOptions(data.options); break;
|
|
||||||
case "start": start(data.bufferSize); break;
|
|
||||||
case "record": record(data.buffer); break;
|
|
||||||
case "finish": finish(); break;
|
|
||||||
case "cancel": cleanup();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
self.postMessage({ command: "loaded" });
|
|
Loading…
Reference in New Issue