Created
March 8, 2014 09:11
-
-
Save chikoski/9427685 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <meta charset="utf-8"> | |
| <title>JS Bin</title> | |
| </head> | |
| <body> | |
| <input type="button" value="start" id="start"> | |
| <input type="button" value="stop" id="stop"> | |
| <audio id="output" controls> | |
| </body> | |
| </html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| (function(window){ | |
| var WORKER_PATH = 'https://raw.github.com/mattdiamond/Recorderjs/master/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; | |
| this.node.onaudioprocess = function(e){ | |
| if (!recording) return; | |
| 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 | |
| }); | |
| }; | |
| 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); | |
| var worker_code = "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\'); /* file length */ view.setUint32(4, 32 + 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;}"; | |
| var stream = null; | |
| var startButton = document.getElementById("start"); | |
| var stopButton = document.getElementById("stop"); | |
| var output = document.getElementById("output"); | |
| var recorder = null; | |
| window.URL = window.URL || window.webkitURL; | |
| navigator.getUserMedia = navigator.getUserMedia || | |
| navigator.webkitGetUserMedia || | |
| navigator.mozGetUserMedia || | |
| navigator.msGetUserMedia; | |
| var bb = new Blob([worker_code]); | |
| var onFail = function(e){ | |
| console.log('Rejected!', e); | |
| }; | |
| var onSuccess = function(s){ | |
| stream = s; | |
| var context = new webkitAudioContext(); | |
| var mediaStreamSource = context.createMediaStreamSource(s); | |
| recorder = new Recorder(mediaStreamSource, { | |
| workerPath: window.URL.createObjectURL(bb) | |
| }); | |
| recorder.record(); | |
| }; | |
| function startRecording() { | |
| if (navigator.getUserMedia) { | |
| navigator.getUserMedia({audio: true}, onSuccess, onFail); | |
| } else { | |
| console.log('navigator.getUserMedia not present'); | |
| } | |
| } | |
| function stopRecording() { | |
| recorder.stop(); | |
| recorder.exportWAV(function(s) { | |
| output.src = window.URL.createObjectURL(s); | |
| }); | |
| } | |
| startButton.addEventListener("click", function(event){ | |
| console.log("start"); | |
| startRecording(); | |
| }); | |
| stopButton.addEventListener("click", function(event){ | |
| console.log("stop"); | |
| stopRecording(); | |
| stream.stop(); | |
| }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment