JSFiddle - React, Tailwind, and code Playground

by juberti

HTML

<button id="playButton"
        onclick="play()">
  â–¶
</button>
<button id="stopButton"
        onclick="stop()"
        disabled>
  Stop
</button>
<br><br>
<button id="stopTheNoiseButton"
        onclick="stopTheNoise()">
  If it gets annoying press this button
<audio>

JavaScript

const playButton = document.querySelector('#playButton');
const stopButton = document.querySelector('#stopButton');
const audioElement = document.querySelector('audio');

const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
let mediaStreamDstNode = null;
let bufferSource = null;

function createBufferSource() {
  const frameCount = 48000;  // 1.0s at 48 kHz.
  const channelCount = 2;
  const buffer = audioCtx.createBuffer(channelCount, frameCount, audioCtx.sampleRate);

  for (let channel = 0; channel < channelCount; channel++) {
    const channelData = buffer.getChannelData(channel);
    for (let i = 0; i < frameCount; i++) {
      // Random values on [-0.1, 0.1].
      channelData[i] = Math.random() * 0.2 - 0.1;
    }
  }

  const source = audioCtx.createBufferSource();
  source.buffer = buffer;
  
  // Wire up source all the way to the output.
  mediaStreamDstNode = mediaStreamDstNode || audioCtx.createMediaStreamDestination();
  audioElement.srcObject = mediaStreamDstNode.stream;  
  source.connect(mediaStreamDstNode);

  return source;
}

function play() {
  bufferSource = createBufferSource();
  bufferSource.onended = () => {
    stop();
  }
  audioElement.play();
  bufferSource.start();
  playButton.setAttribute('disabled', true);
  stopButton.removeAttribute('disabled');
}

function stop() {
  if (!bufferSource) { return; }
  bufferSource.stop();
  bufferSource.disconnect();
  bufferSource = null;

  // After 1 sec, Safari iOS 14 produces noise unless either:
  //     audioElement.srcObject = null;
  //     audioElement.pause();  
  //
  // But these steps shouldn't be needed.
  // We've already stopped and disconnected bufferSource.
  //
  // In contrast, the following browsers work fine:
  //   Safari 13.1.2 (MacOS)
  //   Chrome 85

  playButton.removeAttribute('disabled');
  stopButton.setAttribute('disabled', true);
}

function stopTheNoise() {
  audioElement.srcObject = null;
}