UNPKG

web-audio-recorder-js

Version:

WebAudioRecorder.js is a JavaScript library that records audio input (Web Audio API AudioNode object) and encodes to audio file image (Blob object). It supports three encoding formats.

200 lines (182 loc) 6.55 kB
(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);