IOS WebAudio only works on headphones
Asked Answered
V

4

10

I've been running into an issue now for a while where on some ios devices my webaudio system only seems to work with headphones where as other devices (exact same os, model, etc) the audio plays perfectly fine through the speakers or headphones. I've searched for a solution to this but haven't found anything on this exact issue. The only thing I can think of is that maybe it's an audio channel issue or something.

How can I fix this?

Valkyrie answered 14/1, 2014 at 19:37 Comment(5)
I did read something about it possibly being a headphone jack issue, however I've seen this on brand new devices and have seen it work fine on old / heavily used devices as well. I've also tried toggling mute..no difference.Valkyrie
This. Is. Incredible. I've been trying to figure out why I wasn't hearing anything from one of my webapps on an iPad from our QA lab - from the web inspector I saw that everything was being correctly init'ed from a touch event and that everything was loading. I read your post, plugged in headphones and went into shock. I can only recreate this on an iPad3 (retina) running iOS 7.0.4, and the webapp in question is using CreateJS/SoundJS. The odd thing is - going to webaudioapi.com/samples, playing some samples, and returning to my webapp fixed the sound... until I cleared the cache.Roundel
Okay, it turns out that the side slider switch was set to control mute and it was muted. But that doesn't explain why the sounds began playing after switching sites, since I didn't even realize that the switch existed until just now. Even now I can get web audio sound to play once started while switch-unmuted and then changing to switch-muted.Roundel
Yeah this definitely caused me some headaches. I honestly can't even remember now if we resolved it or not, but I do remember adding the gain node which I think I remember it helping with the issue.Valkyrie
Could it have something to do with the fact that Web Audio is muted until the user taps the screen? See paulbakaus.com/tutorials/html5/web-audio-on-iosLancers
C
25

@Alastair is correct, the mute toggle switch does mute WebAudio, but it does not mute HTML5 tags. Thanks to his work I managed to find a work around for the web which enables WebAudio to play even when the mute toggle switch is on. I'd post this as a comment on his reply, but I don't have the reputation.

In order to play WebAudio you must also play at least one WebAudio sound source node and one HTML5 tag during a user action. It is fine if these sounds are short bits of silence. I found that this self contained code works without any extra files needed:

EDIT 11/29/19: Removed vestigial typescript typedefs. Thanks @Joep. I also realized the code below is woefully out of date and janky. Just consider it an example. Editing this post prompted me to create an open source solution for this. You can see a demo of it here: https://spencer-evans.com/share/github/unmute/ and check out the repo here: https://github.com/swevans/unmute

/**
 * PLEASE DONT USE THIS AS IT IS, THIS IS JUST EXAMPLE CODE.
 * If you want a drop in solution I have a script on git hub
 * Demo:
 * @see https://spencer-evans.com/share/github/unmute/
 * Github Repo:
 * @see https://github.com/swevans/unmute
 */
var isWebAudioUnlocked = false;
var isHTMLAudioUnlocked = false;

function unlock() {
    if (isWebAudioUnlocked  && isHTMLAudioUnlocked) return;

    // Unlock WebAudio - create short silent buffer and play it
    // This will allow us to play web audio at any time in the app
    var buffer = myContext.createBuffer(1, 1, 22050); // 1/10th of a second of silence
    var source = myContext.createBufferSource();
    source.buffer = buffer;
    source.connect(myContext.destination);
    source.onended = function()
    {
        console.log("WebAudio unlocked!");
        isWebAudioUnlocked = true;
        if (isWebAudioUnlocked && isHTMLAudioUnlocked)
        {
            console.log("WebAudio unlocked and playable w/ mute toggled on!");
            window.removeEventListener("mousedown", unlock);
        }
    };
    source.start();

    // Unlock HTML5 Audio - load a data url of short silence and play it
    // This will allow us to play web audio when the mute toggle is on
    var silenceDataURL = "data:audio/mp3;base64,//MkxAAHiAICWABElBeKPL/RANb2w+yiT1g/gTok//lP/W/l3h8QO/OCdCqCW2Cw//MkxAQHkAIWUAhEmAQXWUOFW2dxPu//9mr60ElY5sseQ+xxesmHKtZr7bsqqX2L//MkxAgFwAYiQAhEAC2hq22d3///9FTV6tA36JdgBJoOGgc+7qvqej5Zu7/7uI9l//MkxBQHAAYi8AhEAO193vt9KGOq+6qcT7hhfN5FTInmwk8RkqKImTM55pRQHQSq//MkxBsGkgoIAABHhTACIJLf99nVI///yuW1uBqWfEu7CgNPWGpUadBmZ////4sL//MkxCMHMAH9iABEmAsKioqKigsLCwtVTEFNRTMuOTkuNVVVVVVVVVVVVVVVVVVV//MkxCkECAUYCAAAAFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV";
    var tag = document.createElement("audio");
    tag.controls = false;
    tag.preload = "auto";
    tag.loop = false;
    tag.src = silenceDataURL;
    tag.onended = function()
    {
        console.log("HTMLAudio unlocked!");
        isHTMLAudioUnlocked = true;
        if (isWebAudioUnlocked && isHTMLAudioUnlocked)
        {
            console.log("WebAudio unlocked and playable w/ mute toggled on!");
            window.removeEventListener("mousedown", unlock);
        }
    };
    var p = tag.play();
    if (p) p.then(function(){console.log("play success")}, function(reason){console.log("play failed", reason)});
}

window.addEventListener("mousedown", unlock);
Custard answered 19/10, 2017 at 22:42 Comment(8)
It is disgusting that this is possible or necessary. Thank you for sharing this awful thing.Belize
The solution works, but Safari's console says "Unhandled Promise Rejection: NotAllowedError: The request is not allowed by the user agent or the platform in the current context, possibly because the user denied permission." I guess adding an error listener could solve that, any idea how?Candleberry
@Candleberry I believe chrome and safari are now requiring that promises returned from .play() have resolution handlers. Not all browsers support promises. You have to check if one was returned and can't really count on them being supported consistently or behaving consistently. That said, the promise is most likely being rejected because unlock is being called outside of a user interaction handler. I updated the code to suppress those promise errors.Custard
I am loading this script as unmute.js into my website and I get following error in the console: SyntaxError: unexpected token: ':' unmute.js:27:22Marrowbone
@Marrowbone Thanks for pointing that out. It was some typescript code I accidentally left in. I updated the post and created an open source solution for you. LMK if you have any trouble with it.Custard
Thanks a lot! I will test the unmute.min.js asap and let you know if it worked out for me!Marrowbone
It does help in the silent mode and it doesn't with background tabs or locked iPhone (iPhone 15 Pro, iOS 17.2.1). Probably they fixed/changed something. Anyway, upvoting because it makes better than it was!Hyetography
As of today (ios 17.2 ipad) this only makes my audio audible with silence on, whilst the tag is playing (so ~1 sec every tap). Setting loop=true allows my audiocontext audio to continiously playKatherinkatherina
P
12

This is likely because the iPhone's side switch is on "mute". It's very confusing - HTML5 <audio> tags still play fine when the phone is muted, but WebAudio does not. Why? Who knows. But it's a restriction I currently haven't found a way around.

Placate answered 18/10, 2014 at 13:20 Comment(2)
Yes, on my web app using soundmanager2, I found that the iPhone's side switch on mute was causing sound to not play through speakers. Thank you!Scarlett
There is a solution, please see my answer.Custard
J
2

If the iPhone mute button is down, meaning that the iPhone is muted, what is played through Web Audio Api will be muted.

Unfortunately there is no way to check if that physical button (located on the left edge towards the top of the iPhone) is on or off through Javascript.

This issue is completely independent from the fact that in iOS Safari the audio has to be started by a user action for it to be unmuted. There are some tricks that can be done to overcome that fact, including the one suggested by here Spencer, were you use "any action or a specific action" started by the user to "play" a silent audio file to allow subsequently playing audio files to play unmuted.

Jerkin answered 15/7, 2018 at 16:56 Comment(1)
actually the solution to getting around the mute button is to play html and web audio at the same time. Resuming on user interaction is just to make sure that audio is "unlocked" all the time.Custard
L
0

had same issue, and finally understood problem.

indeed WebView don't play sound on internal speakers if phone is in mute.

when i dig deeper i found a workaround :) original post => https://mcmap.net/q/593998/-how-to-play-a-sound-using-avaudioplayer-when-in-silent-mode-in-iphone

do {
    try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
    //print("AVAudioSession Category Playback OK")
    do {
        try AVAudioSession.sharedInstance().setActive(true)
        //print("AVAudioSession is Active")
    } catch _ as NSError {
        //print(error.localizedDescription)
    }
} catch _ as NSError {
    //print(error.localizedDescription)
}
Luhey answered 25/5, 2017 at 9:39 Comment(1)
This is an objective c solution. The poster is asking about web audio.Custard

© 2022 - 2024 — McMap. All rights reserved.