Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import InlineWorker from 'inline-worker';
- export class Recorder {
- config = {
- sampleRate: 16000,
- bufferLen: 4096,
- numChannels: 2,
- mimeType: 'audio/pcm'
- };
- _stream;
- recording = false;
- callbacks = {
- getBuffer: [],
- exportWAV: []
- };
- constructor(source, stream, cfg) {
- this._stream = stream;
- 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 = (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
- let self = {};
- this.worker = new InlineWorker(function () {
- let recLength = 0,
- recBuffers = [],
- sampleRate,
- contextRate,
- numChannels;
- 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;
- contextRate = config.contextRate;
- numChannels = config.numChannels;
- initBuffers();
- }
- function record(inputBuffer) {
- for (var channel = 0; channel < numChannels; channel++) {
- recBuffers[channel].push(inputBuffer[channel]);
- }
- recLength += inputBuffer[0].length;
- }
- function getBuffer() {
- let buffers = [];
- for (let channel = 0; channel < numChannels; channel++) {
- buffers.push(mergeBuffers(recBuffers[channel], recLength));
- }
- this.postMessage({command: 'getBuffer', data: buffers});
- }
- function clear() {
- recLength = 0;
- recBuffers = [];
- initBuffers();
- }
- function initBuffers() {
- for (let channel = 0; channel < numChannels; channel++) {
- recBuffers[channel] = [];
- }
- }
- function mergeBuffers(recBuffers, recLength) {
- let result = new Float32Array(recLength);
- let offset = 0;
- for (let i = 0; i < recBuffers.length; i++) {
- result.set(recBuffers[i], offset);
- offset += recBuffers[i].length;
- }
- return result;
- }
- function interleave(inputL, inputR){
- var result = new Float32Array(inputL.length);
- for (var i = 0; i < inputL.length; ++i)
- result[i] = 0.5 * (inputL[i] + inputR[i]);
- return result;
- }
- function floatTo16BitPCM(output, offset, input) {
- for (let i = 0; i < input.length; i++, offset += 2) {
- let 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 (let i = 0; i < string.length; i++) {
- view.setUint8(offset + i, string.charCodeAt(i));
- }
- }
- function exportWAV(type) {
- let bufferL = mergeBuffers(recBuffers[0], recLength);
- let bufferR = mergeBuffers(recBuffers[1], recLength);
- let interleaved = interleave(bufferL, bufferR);
- let downsampledBuffer = downsampleBuffer(interleaved);
- let dataview = encodeWAV(downsampledBuffer);
- let audioBlob = new Blob([ dataview ], {
- type : type
- });
- this.postMessage({command: 'exportWAV', data: audioBlob});
- }
- function downsampleBuffer(buffer) {
- if (sampleRate === contextRate) {
- return buffer;
- }
- if (sampleRate > contextRate) {
- throw "downsampling rate show be smaller than original sample rate";
- }
- let sampleRateRatio = contextRate / sampleRate;
- let newLength = Math.round(buffer.length / sampleRateRatio);
- let result = new Float32Array(newLength);
- let offsetResult = 0;
- let offsetBuffer = 0;
- while (offsetResult < result.length) {
- let nextOffsetBuffer = Math.round((offsetResult + 1) * sampleRateRatio);
- let accum = 0, count = 0;
- for (let i = offsetBuffer; i < nextOffsetBuffer && i < buffer.length; i++) {
- accum += buffer[i];
- count++;
- }
- result[offsetResult] = accum / count;
- offsetResult++;
- offsetBuffer = nextOffsetBuffer;
- }
- return result;
- }
- function encodeWAV(samples) {
- let buffer = new ArrayBuffer(44 + samples.length * 2);
- let 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, 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: {
- contextRate: this.context.sampleRate,
- sampleRate: this.config.sampleRate,
- numChannels: this.config.numChannels
- }
- });
- this.worker.onmessage = (e) => {
- let cb = this.callbacks[e.data.command].pop();
- if (typeof cb == 'function') {
- cb(e.data.data);
- }
- };
- }
- record() {
- this.recording = true;
- }
- stop() {
- this.recording = false;
- }
- clear() {
- this.worker.postMessage({command: 'clear'});
- }
- 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'});
- }
- 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
- });
- }
- dispose(){
- try{
- if(this._stream){
- this._stream.getAudioTracks()[0].stop();
- }
- }catch(e){}
- }
- static forceDownload(blob, filename) {
- let url = (window.URL || window.webkitURL).createObjectURL(blob);
- let link = window.document.createElement('a');
- link.href = url;
- link.download = filename || 'output.wav';
- let click = document.createEvent("Event");
- click.initEvent("click", true, true);
- link.dispatchEvent(click);
- }
- }
- // Run the stuff.
- const lexruntime = new LexRuntime({
- apiVersion: '2016-11-28',
- accessKeyId: '##########',
- secretAccessKey: '##########',
- region: '##########'
- });
- navigator.getUserMedia({audio: true}, stream => {
- window.AudioContext = window.AudioContext || window.webkitAudioContext;
- navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia;
- window.URL = window.URL || window.webkitURL;
- const audio_context = new AudioContext;
- const input = audio_context.createMediaStreamSource(stream);
- input.connect(audio_context.destination);
- const recorder = new Recorder(input, stream);
- this._recorder.record();
- setTimeout(() => {
- try{
- this._recorder.stop();
- await new Promise((resolve, reject){
- this._recorder.exportWAV(buffer => {
- this._recorder.clear();
- lexruntime.postContent({
- botAlias: 'your bot alias', /* required */
- botName: 'your bot name', /* required */
- contentType: 'audio/x-l16; sample-rate=16000; channel-count=1', /* required */
- inputStream: buffer, /* required */
- userId: `user-${Date.now()}`, /* required */
- accept: 'audio/*'
- }, (err, data) => {
- if(err) return reject(err);
- resolve(data);
- });
- });
- });
- }catch(e){
- console.log(e);
- }
- }, 3000);
- }, err => console.error(err));
Add Comment
Please, Sign In to add comment