Search code examples
javascriptiosipadsafariweb-audio-api

iOS on iPad Disabling Web Audio API


Please note: This issue was fixed Nov 2016 in iOS 10.1.1. Thank you Apple!

I am unable to play any audio through Web Audio API on iPad. This is using the latest version of iOS (since 9.3.2 - currently 10.1).

Test Case

Here is a bare-bones test I have set up (code below). None of the tests work on iPad for me. No problems on iPhone or other compatible browsers. There is another test page here (from William Malone). None of those sounds play on three iPads tried. Finally this game uses Web Audio API (thanks Derek) and is also muted.

If you are reading this on iPad, please try that bare-bones test and report in the comments (or contact me privately) if load, followed by play, works and plays sound. Please include iOS version number (Settings -> General -> About -> Version).

Any insights or feedback would be much appreciated!

Here is the test code:

<!DOCTYPE html>
<html>
<head>
    <title>Web Audio API - iPad Test</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script src="./jquery-2.2.3.min.js"></script>
    <style>
        section { padding:10px; margin:0 0 10px; background:DarkSeaGreen }
        a { cursor:pointer; display:block; background: DarkSeaGreen; padding:10px; margin:0 0 3px }
    </style>
</head>
<body>
    <script>
        var app = {};
        $(document).ready(function() {
            // Do we have Web Audio API?
            try {
                window.AudioContext = window.AudioContext || window.webkitAudioContext;
                audioContext = new AudioContext();
            } catch (e) {
                alert('Web Audio API is not supported in this browser');
            }
            $('#loadClick').on('click',function(){load('./ipad_test.mp3')});
            $('#playTouchEnd').on('touchend',play);
            $('#playTouchStart').on('touchstart',play);
            $('#playClick').on('click',play);
            $('#unlockTouchEnd').on('touchend',unlock);
            $('#unlockTouchStart').on('touchstart',unlock);
            $('#unlockClick').on('click',unlock);
        });
        function unlock() {
            // play empty buffer to unmute audio
            var buffer = audioContext.createBuffer(1, 1, 22050);
            var source = audioContext.createBufferSource();
            source.audioContext = buffer;
            source.connect(audioContext.destination);
            source.start(0);
            $('#messages').append('<p>Unlocked.</p>');
        }
        function load(file) {
            if(app.loaded) {
                $('#messages').append('<p>Already loaded.</p>');
                return;
            }
            var request = new XMLHttpRequest();
            request.open ('GET', file, true);
            request.responseType = 'arraybuffer';
            request.onload = function () {
                audioContext.decodeAudioData(
                        request.response,
                        function (buffer) {
                            app.buffer = buffer;
                            $('#messages').append('<p>Loaded.</p>');
                            app.loaded = 1;
                        },
                        function(){alert('Load error.');}
                )
            };
            request.send();
        }
        function play() {
            if(!app.loaded) {
                $('#messages').append('<p>Please load before playing.</p>');
                return;
            }
            var sourceNode = audioContext.createBufferSource();
            sourceNode.buffer = app.buffer;
            sourceNode.connect (audioContext.destination);
            sourceNode.start(0);
            $('#messages').append('<p>Playing.</p>');
        }
    </script>
    <h1>Web Audio API Test</h1>
    <p>Unlock should not be needed at all, but offers an alternative way to try and get the audio going. Load and then play should be all any compatible device needs. Refresh to start again.</p>
    <a id="unlockTouchEnd">Unlock (touchend)</a>
    <a id="unlockTouchStart">Unlock (touchstart)</a>
    <a id="unlockClick">Unlock (click)</a>
    <a id="loadClick" style="background:#c0c0c0">Load (click)</a>
    <a id="playTouchEnd">Play Audio (touchend)</a>
    <a id="playTouchStart">Play Audio (touchstart)</a>
    <a id="playClick">Play Audio (click)</a>
    <section id="messages"></section>
</body>
</html>

Background

The Apple documentation makes it clear that audio must be initiated from a user action for Web Audio API to work. They state:

Note: On iOS, the Web Audio API requires sounds to be triggered from an explicit user action, such as a tap. Calling noteOn() from an onload event will not play sound.

There are a number of threads online about problems with this, the latest being autumn 2015 with iOS 9.0 to 9.2:

They suggest firing audio from a touchstart event to unlock iOS audio (or touchend while there were issues at that time). I have tried all of the suggested techniques and can't get touchstart, touchend or click to work. The audio is always muted on iPad.


Solution

  • Apple fixed this issue in iOS 10.1.1 November 2016. Thank you Apple!