UNPKG

@vikasietum_tecknology/record-rtc

Version:

record-rtc is a library based on recordrtc library. In this forked version of the original library we have optimized the memory management. The video recording is stored in IndexDB in chunks.

316 lines (249 loc) 10.3 kB
<style> html, body { margin: 0!important; padding: 0!important; } </style> <title>Process RAW PCM data using RecordRTC</title> <h1>Process RAW PCM data using RecordRTC</h1> <br> <button id="btn-start-recording">Start Recording</button> <button id="btn-stop-recording" disabled>Stop Recording</button> <hr> <audio controls autoplay playsinline></audio> <script src="/RecordRTC.js"></script> <script> var audio = document.querySelector('audio'); function captureMicrophone(callback) { navigator.mediaDevices.getUserMedia({ audio: true }).then(function(microphone) { callback(microphone); }).catch(function(error) { alert('Unable to capture your microphone. Please check console logs.'); console.error(error); }); } function stopRecordingCallback() { // ------------------------------------------------------------ // get access to StereoAudioRecorder object (name as "internal-recorder") // ------------------------------------------------------------ var internalRecorder = recorder.getInternalRecorder(); // ------------------------------------------------------------ // get left and right audio channels // ------------------------------------------------------------ var leftchannel = internalRecorder.leftchannel; var rightchannel = internalRecorder.rightchannel; // ------------------------------------------------------------ // create your own WAV // ------------------------------------------------------------ mergeLeftRightBuffers({ desiredSampRate: internalRecorder.desiredSampRate, sampleRate: internalRecorder.sampleRate, numberOfAudioChannels: internalRecorder.numberOfAudioChannels, internalInterleavedLength: internalRecorder.recordingLength, leftBuffers: leftchannel, rightBuffers: internalRecorder.numberOfAudioChannels === 1 ? [] : rightchannel }, function(buffer, view) { // ------------------------------------------------------------ // here is your own WAV (generated by your own codes) // ------------------------------------------------------------ var blob = new Blob([buffer], { type: 'audio/wav' }); audio.srcObject = null; audio.src = URL.createObjectURL(blob); }); recorder.microphone.stop(); } var recorder; // globally accessible document.getElementById('btn-start-recording').onclick = function() { this.disabled = true; captureMicrophone(function(microphone) { audio.srcObject = microphone; recorder = RecordRTC(microphone, { type: 'audio', recorderType: StereoAudioRecorder, // numberOfAudioChannels: 1 }); recorder.startRecording(); // release microphone on stopRecording recorder.microphone = microphone; document.getElementById('btn-stop-recording').disabled = false; }); }; document.getElementById('btn-stop-recording').onclick = function() { this.disabled = true; recorder.stopRecording(stopRecordingCallback); }; // ------------------------------------------------------------ // below two methods are copied from dev/StereoAudioRecorder.js // just to explain how to process RAW PCM data // ------------------------------------------------------------ function mergeLeftRightBuffers(config, callback) { function mergeAudioBuffers(config, cb) { var numberOfAudioChannels = config.numberOfAudioChannels; // todo: "slice(0)" --- is it causes loop? Should be removed? var leftBuffers = config.leftBuffers.slice(0); var rightBuffers = config.rightBuffers.slice(0); var sampleRate = config.sampleRate; var internalInterleavedLength = config.internalInterleavedLength; var desiredSampRate = config.desiredSampRate; if (numberOfAudioChannels === 2) { leftBuffers = mergeBuffers(leftBuffers, internalInterleavedLength); rightBuffers = mergeBuffers(rightBuffers, internalInterleavedLength); if (desiredSampRate) { leftBuffers = interpolateArray(leftBuffers, desiredSampRate, sampleRate); rightBuffers = interpolateArray(rightBuffers, desiredSampRate, sampleRate); } } if (numberOfAudioChannels === 1) { leftBuffers = mergeBuffers(leftBuffers, internalInterleavedLength); if (desiredSampRate) { leftBuffers = interpolateArray(leftBuffers, desiredSampRate, sampleRate); } } // set sample rate as desired sample rate if (desiredSampRate) { sampleRate = desiredSampRate; } // for changing the sampling rate, reference: // http://stackoverflow.com/a/28977136/552182 function interpolateArray(data, newSampleRate, oldSampleRate) { var fitCount = Math.round(data.length * (newSampleRate / oldSampleRate)); //var newData = new Array(); var newData = []; //var springFactor = new Number((data.length - 1) / (fitCount - 1)); var springFactor = Number((data.length - 1) / (fitCount - 1)); newData[0] = data[0]; // for new allocation for (var i = 1; i < fitCount - 1; i++) { var tmp = i * springFactor; //var before = new Number(Math.floor(tmp)).toFixed(); //var after = new Number(Math.ceil(tmp)).toFixed(); var before = Number(Math.floor(tmp)).toFixed(); var after = Number(Math.ceil(tmp)).toFixed(); var atPoint = tmp - before; newData[i] = linearInterpolate(data[before], data[after], atPoint); } newData[fitCount - 1] = data[data.length - 1]; // for new allocation return newData; } function linearInterpolate(before, after, atPoint) { return before + (after - before) * atPoint; } function mergeBuffers(channelBuffer, rLength) { var result = new Float64Array(rLength); var offset = 0; var lng = channelBuffer.length; for (var i = 0; i < lng; i++) { var buffer = channelBuffer[i]; result.set(buffer, offset); offset += buffer.length; } return result; } function interleave(leftChannel, rightChannel) { var length = leftChannel.length + rightChannel.length; var result = new Float64Array(length); var inputIndex = 0; for (var index = 0; index < length;) { result[index++] = leftChannel[inputIndex]; result[index++] = rightChannel[inputIndex]; inputIndex++; } return result; } function writeUTFBytes(view, offset, string) { var lng = string.length; for (var i = 0; i < lng; i++) { view.setUint8(offset + i, string.charCodeAt(i)); } } // interleave both channels together var interleaved; if (numberOfAudioChannels === 2) { interleaved = interleave(leftBuffers, rightBuffers); } if (numberOfAudioChannels === 1) { interleaved = leftBuffers; } var interleavedLength = interleaved.length; // create wav file var resultingBufferLength = 44 + interleavedLength * 2; var buffer = new ArrayBuffer(resultingBufferLength); var view = new DataView(buffer); // RIFF chunk descriptor/identifier writeUTFBytes(view, 0, 'RIFF'); // RIFF chunk length view.setUint32(4, 44 + interleavedLength * 2, true); // RIFF type writeUTFBytes(view, 8, 'WAVE'); // format chunk identifier // FMT sub-chunk writeUTFBytes(view, 12, 'fmt '); // format chunk length view.setUint32(16, 16, true); // sample format (raw) view.setUint16(20, 1, true); // stereo (2 channels) view.setUint16(22, numberOfAudioChannels, true); // sample rate view.setUint32(24, sampleRate, true); // byte rate (sample rate * block align) view.setUint32(28, sampleRate * 2, true); // block align (channel count * bytes per sample) view.setUint16(32, numberOfAudioChannels * 2, true); // bits per sample view.setUint16(34, 16, true); // data sub-chunk // data chunk identifier writeUTFBytes(view, 36, 'data'); // data chunk length view.setUint32(40, interleavedLength * 2, true); // write the PCM samples var lng = interleavedLength; var index = 44; var volume = 1; for (var i = 0; i < lng; i++) { view.setInt16(index, interleaved[i] * (0x7FFF * volume), true); index += 2; } if (cb) { return cb({ buffer: buffer, view: view }); } postMessage({ buffer: buffer, view: view }); } if (!isChrome) { // its Microsoft Edge mergeAudioBuffers(config, function(data) { callback(data.buffer, data.view); }); return; } var webWorker = processInWebWorker(mergeAudioBuffers); webWorker.onmessage = function(event) { callback(event.data.buffer, event.data.view); // release memory URL.revokeObjectURL(webWorker.workerURL); }; webWorker.postMessage(config); } function processInWebWorker(_function) { var workerURL = URL.createObjectURL(new Blob([_function.toString(), ';this.onmessage = function (eee) {' + _function.name + '(eee.data);}' ], { type: 'application/javascript' })); var worker = new Worker(workerURL); worker.workerURL = workerURL; return worker; } </script> <footer style="margin-top: 20px;"><small id="send-message"></small></footer> <script src="https://www.webrtc-experiment.com/common.js"></script>