Search code examples
javascriptsafariweb-audio-api

Getting "undefined is not an object" when chaining .connect() methods in Safari


I am trying to chain .connect methods to my AudioNode. Just like in this example: https://googlechrome.github.io/samples/webaudio-method-chaining/

It works in Chrome and Firefox but I get "TypeError: undefined is not an object (evaluating 'source.connect(gainNode).connect')" in Safari. Is there any way to solve this?

const $audioPlayer = document.querySelector('.audio-player')
const audio = new AudioContext()
const gainNode = audio.createGain()
const source = audio.createMediaElementSource($audioPlayer)
source.connect(gainNode).connect(audio.destination)

Solution

  • Safari's implementation of the Web Audio API is not only prefixed but unfortunately also very outdated. The connect() method does always return undefined in Safari.

    To make your example work in Safari you can rewrite line 2 and split line 5 into two calls.

    const $audioPlayer = document.querySelector('.audio-player'); // unchanged
    
    const audio = new webkitAudioContext();
    
    const gainNode = audio.createGain(); // unchanged
    const source = audio.createMediaElementSource($audioPlayer); // unchanged
    
    source.connect(gainNode);
    gainNode.connect(audio.destination);
    

    If you want to write your code as if it targets the newest iteration of the Web Audio API you can use a library.

    I'm the author of standardized-audio-context. There is also web-audio-api-shim which polyfills the connect() method as well.

    To use the standardized-audio-context library you would just need to add an import statement to your code.

    import { AudioContext } from 'standardized-audio-context';
    
    // everything else can be left unchanged
    const $audioPlayer = document.querySelector('.audio-player');
    const audio = new AudioContext();
    const gainNode = audio.createGain();
    const source = audio.createMediaElementSource($audioPlayer);
    
    source
        .connect(gainNode)
        .connect(audio.destination);