Multi ASR Demo (TS)

by juberti

HTML

<!DOCTYPE html>
<script type="worklet">
  registerProcessor('my-processor', class param extends AudioWorkletProcessor {
  process(inputs, outputs, parameters) {
    const input = inputs[0];
    const output = outputs[0];

    for (let channel = 0; channel < input.length; ++channel) {
      const inputChannel = input[channel];
      const outputChannel = output[channel];

      for (let i = 0; i < inputChannel.length; ++i) {
        outputChannel[i] = inputChannel[i];
      }
    }

    // Copy the input data to a new Float32Array
    const data = new Float32Array(input[0]);

    // Post the data back to the main thread
    this.port.postMessage(data);

    return true;
  }
});
</script>
<h3>Deepgram</h3>
<textarea cols="80" rows="5" id="deepgram"></textarea>
<h3>Soniox</h3>
<textarea cols="80" rows="5" id="soniox"></textarea>
<h3>Gladia</h3>
<textarea cols="80" rows="5" id="gladia"></textarea>
<h3>AssemblyAI</h3>
<textarea cols="80" rows="5" id="aai"></textarea>
<br>
<button onclick="start(1)">Start</button>
<button onclick="stop()">Stop</button>

TypeScript

const workerBlob = new Blob([
  document.querySelector('script[type=worklet]').textContent
], {
  type: "text/javascript"
});

class MicManager implements EventTarget {
  constructor() {
  	this.listeners = {};
  	this.outBuf = [];
  }
  addEventListener(type: string, listener) {
  	if (!this.listeners[type]) {
    	this.listeners[type] = [];
    }
    this.listeners[type].push(listener);
  }
  removeEventListener(type: string, listener) {
  	const index = this.listeners[type].indexOf(listener);
		if (index != -1) {
    	this.listeners[type].splice(index, 1);
    }
  }
  async startMic(timeslice: number) {
 		this.context = new window.AudioContext();
  	this.startGraph(await navigator.mediaDevices.getUserMedia({audio: true}), timeslice); 
  }
  async startFile(url: string, timeslice: number) {
    this.context = new window.AudioContext();
  	const response = await fetch(url);  
    const blob = await response.blob();
  	const audioElement = new Audio();
		audioElement.src = URL.createObjectURL(blob);
		audioElement.addEventListener('canplaythrough', () => {
		  audioElement.play();
      this.startGraph(audioElement.captureStream(), timeslice);
		});
  }
  private async startGraph(stream: MediaStream, timeslice: number) {
  	this.numSilentFrames = 0;
	  this.stream = stream; 
    const workerUrl = window.URL.createObjectURL(workerBlob);
    await this.context.audioWorklet.addModule(workerUrl);
    this.processorNode = new AudioWorkletNode(this.context, 'my-processor');
    this.processorNode.port.onmessage = event => {
     	this.outBuf.push(event.data);
			const bufDuration = 128 * this.outBuf.length / this.sampleRate() * 1000;
      if (bufDuration >= timeslice) {
      	const chunkEvent = new CustomEvent("chunk", {detail: this.makePcmChunk(this.outBuf}));
	      this.dispatchEvent(chunkEvent);
	      this.outBuf = [];
      }
    };
		const source = this.context.createMediaStreamSource(stream);    
   ...