gUM browser+audioCapture

by jib1

HTML

<video id="video" controls width="160" height="120" autoplay></video><br>
<button id="button">Go!</button>
<div id="container"></div>
<div id="div"></div>

JavaScript

const console = {log: msg => div.innerHTML += `${msg}<br>`};

// Requires media.getusermedia.audiocapture.enabled=true
button.onclick = async () => {
  try {
    const stream =await navigator.mediaDevices.getDisplayMedia({
      video: true,
      audio: true,
    });
    video.srcObject = stream;
    spectrum(stream);
  } catch (e) {
    console.log(e);
  }
};

function spectrum(stream) {
  const [track] = stream.getAudioTracks();
  if (!track) return stream;
  const audioCtx = new AudioContext();
  const analyser = audioCtx.createAnalyser();
  audioCtx.createMediaStreamSource(stream).connect(analyser);
  track.onended = () => audioCtx.close();
  track.stop_ = track.stop;
  track.stop = () => track.stop_(audioCtx.close());
  const canvas = container.appendChild(document.createElement("canvas"));
  canvas.width = video.clientWidth;
  canvas.height = 200;
  const ctx = canvas.getContext("2d");
  const data = new Uint8Array(canvas.width);
  ctx.strokeStyle = 'rgb(0, 125, 0)';

  setInterval(() => {
    ctx.fillStyle = "#a0a0a0";
    ctx.fillRect(0, 0, canvas.width, canvas.height);
 
    analyser.getByteFrequencyData(data);
    ctx.lineWidth = 2;
    let x = 0;
    for (let d of data) {
      const y = canvas.height - (d / 128) * canvas.height / 4;
      const c = Math.floor((x*255)/canvas.width);
      ctx.fillStyle = `rgb(${c},0,${255-x})`;
      ctx.fillRect(x++, y, 2, canvas.height - y)
    }

    analyser.getByteTimeDomainData(data);
    ctx.lineWidth = 5;
    ctx.beginPath();
    x = 0;
    for (let d of data) {
      const y = canvas.height - (d / 128) * canvas.height / 2;
      x ? ctx.lineTo(x++, y) : ctx.moveTo(x++, y);
    }
    ctx.stroke();
  }, 1000 * canvas.width / audioCtx.sampleRate);
  return stream;
};

function log(msg) { div.innerHTML += "<br>" + msg; }