mathiasvr / audio-oscilloscope

:musical_note: Waveform audio visualizer for the HTML5 canvas.
MIT License
92 stars 13 forks source link

Safari compatibility #7

Closed animaux closed 4 years ago

animaux commented 4 years ago

Hy and thanks!

I am trying to make this work on Apple Safari with a html-audio-element as source. It works fine in Mozilla.

Apparently there is a compatibility problem with AudioContext that needs it’s prefixed version webkitAudioContext for Safari. I’m using a workaround that makes the errors go away in Safari.

But sadly the audio output stays mute and the waveform stands still.

Here what I am using:

<!DOCTYPE html>
<html>
<head>
  <title>Oscilloscope</title>
  <style>
    body {
      margin: 0;
      background-color: White;
      color: Black;
      text-align: center;
    }
    canvas {
      display: block;
    }

  </style>
</head>
<body>

  <audio preload="metadata" controls="controls" src="test.mp3"></audio>

  <script src="oscilloscope.umd.js"></script>
  <script src="audio-element.js"></script>

  <script>
    document.addEventListener('DOMContentLoaded', function() {
      startExample();
    })
  </script>
</body>
</html>
function startExample () {

    context = new (window.AudioContext || window.webkitAudioContext)();
    var canvas = document.createElement('canvas')
    canvas.width = window.innerWidth
    canvas.height = window.innerHeight - 50
    document.body.appendChild(canvas)
    var audio = document.querySelector('audio')
    var source = context.createMediaElementSource(audio)

    // attach oscilloscope
    var scope = new Oscilloscope(source)

    // reconnect audio output to speakers
    source.connect(context.destination)

    // customize drawing options
    var ctx = canvas.getContext('2d')
    ctx.lineWidth = 2
    ctx.strokeStyle = '#4096b3'

    // start default animation loop
    scope.animate(ctx)
}
animaux commented 4 years ago

OK, apparently this can be fixed by activating the whole thing with a user-click, see here. The click listener can also be assigned to the audio element itself, because this would need to be clicked to start playback anyway:

document.querySelector('audio').addEventListener("click", startExample);
mathiasvr commented 4 years ago

Hi, yes I also noticed this and updated the examples a while ago. Google Chrome is also now requiring a user action before audio can be played. Assigning the click listener to the audio element sounds like a good idea, I don't think there's much more we can do about it in this module.

animaux commented 4 years ago

Takk Mathias!

animaux commented 4 years ago

Apparently addEventListener("click",…) is not working with audio elements in Mozilla. Using el.addEventListener('play', startOsc); is not working in Safari …

animaux commented 4 years ago

Here’s an example that works with multiple <audio>-elements in current Safari, Firefox and Edge versions:


<script src="/js/oscilloscope.umd.js"></script>
<script>
  document.addEventListener('DOMContentLoaded', function() {
    // start audio osc
    function startOsc() {
      context = new (window.AudioContext || window.webkitAudioContext)();
      var canvas = document.createElement('canvas');
      canvas.width = 320
      canvas.height = 160
      this.parentNode.insertBefore(canvas, this);

      // select this audioelement as source
      var source = context.createMediaElementSource(this)

      // attach oscilloscope
      var scope = new Oscilloscope(source)

      // reconnect audio output to speakers
      source.connect(context.destination)

      // customize drawing options
      var ctx = canvas.getContext('2d')
      ctx.lineWidth = 4
      ctx.lineCap = 'round'
      ctx.lineJoin = 'round'
      ctx.strokeStyle = '#4096b3'

      // start default animation loop
      scope.animate(ctx)

      // init only once!
      this.removeEventListener('play', startOsc, false);
      this.removeEventListener('click', startOsc, false);
    }
    // bind osc to audio elements on click
    var audios = document.querySelectorAll('audio');
    Array.prototype.forEach.call(audios, function(el, i){
      el.addEventListener('play', startOsc, false);
      el.addEventListener('click', startOsc, false);
    });
  });
</script>```