annnoyboy / YouTube Waveform

// ==UserScript==
// @name         YouTube Waveform
// @version      1.0
// @description  Visualizes the audio of the YouTube video's audio.
// @author       vnullptr
// @match        https://www.youtube.com/watch*
// @grant        none
// @start-at     document-end
// @license      MPL-2.0
// @copyright    2019, annnoyboy (https://openuserjs.org/users/annnoyboy)
// ==/UserScript==

// Already runned or not?
var initWaves = false;

if (!initWaves) {
  // 'use strict';
  initWaves = true;

  Math.map =
    (x, a, b, c, d) =>
    c + (x - a) / (b - a) * (d - c);

  /* The viewport of the canvas */
  var width = 300,
    height = 150;

  var video = document.querySelector('video');
  var canvasCont = document.createElement('div');
  var canvas = document.createElement('canvas');

  canvasCont.classList.add('html5-video-info-panel');
  canvasCont.appendChild(canvas);

  document.querySelector('.html5-video-container')
    .appendChild(canvasCont);

  /* Set the canvas viewport */
  canvas.width = width;
  canvas.height = height;
  canvasCont.width = `${width}px`;
  canvasCont.height = `${width}px`;

  var audioContext = new AudioContext();
  var audioSource = audioContext.createMediaElementSource(video);
  var audioAnalyser = audioContext.createAnalyser();

  audioSource.connect(audioAnalyser);
  audioAnalyser.connect(audioContext.destination);

  var canvasContext = canvas.getContext('2d');
  var WIDTH = canvas.width,
    HEIGHT = canvas.height;

  /* The standard size of the WaveForm */
  audioAnalyser.fftSize = 1024;

  canvasContext.fillStyle = "rgb(255, 0, 0)";
  canvasContext.strokeStyle = "rgb(255, 0, 0)";
  canvasContext.lineJoin = "miter";

  var binCount = audioAnalyser.fftSize;
  var waveform = new Float32Array(binCount);
  var lineSpace = WIDTH / binCount;

  (function () {
    requestAnimationFrame(arguments.callee);

    /* The coordinates of the canvas lines. */
    var x = 0,
      y = 0;

    audioAnalyser.getFloatTimeDomainData(waveform);
    canvasContext.clearRect(0, 0, WIDTH, HEIGHT);

    canvasContext.beginPath();
    waveform.forEach((bin, idx) => {
      // y = (bin < 0) ? Math.abs(bin) * HEIGHT : bin * HEIGHT;
      y = Math.map(bin, -1.0, +1.0, +0.0, +HEIGHT);

      if (idx === 0)
        canvasContext.moveTo(x, y);
      else
        canvasContext.lineTo(x, y);

      // canvasContext.fillRect(x, HEIGHT/2 - y/2, sliceWidth, y);
      x += lineSpace;
    });
    canvasContext.stroke();
  })();
}