0

I am trying to play a beep sound a minute after user has come on the page of my website. I found the solution here https://stackoverflow.com/a/18628124/912359

Here's my code:

$(document).ready(function(){
    setTimeout(function () {
        try{
            if(!$(".facebook-chat").hasClass("active")){
                $(".facebook-chat").addClass("active");
                var audio = new Audio("/sound/chat.mp3");
                audio.play();
            }
        }catch(e){

        }
    }, 60000);
}):

This throws an exception:

Uncaught (in promise) DOMException

Strangely, once I load the sound file separately in my browser and come back to the page, it works perfectly. Any ideas how I can fix it.

[Edit] The issue is that user has to interact with the browser before the sound can be played. So I put the same code under click event of the body and it works. But the same doesn't work on scroll event either. I guess chrome doesn't consider scroll a user interaction. Can anyone add what other interactions can be used to trigger this?

Also, how is it working if I load the audio file in a separate window and come back to my page.

Sourabh
  • 1,757
  • 6
  • 21
  • 43
  • Does it work if you remove the `setTimeout`? Does it work consistently if you play it in an `onClick` handler? – Halcyon Feb 14 '19 at 21:08
  • Is it because you are trying to play it before it has loaded? – James T Feb 14 '19 at 21:11
  • @Halcyon I tried it with a scroll event without timeout. Same problem. – Sourabh Feb 14 '19 at 21:16
  • @JamesT I think so.. but I am not sure how to fix it. – Sourabh Feb 14 '19 at 21:16
  • By the way, your `try...catch` is not going to catch the rejected promise returned by `audio.play()`, unless you change your `setTimeout()` callback to an `async function` and `await audio.play()` instead. – Patrick Roberts Feb 14 '19 at 21:21
  • @PatrickRoberts yes I put it there just so it doesn't break anything else. Thanks for the tip though. :) – Sourabh Feb 14 '19 at 21:23
  • There's likely some sort of 'onload' or 'oncanplay' event you'll want to listen for before playing the audio. I'd check first that the /sound/chat.mp3 file is available. What you said about loading the file separately makes me think that maybe the file isn't available correctly in the first place. I'd then check to make sure that when using the `Audio` API that you aren't supposed to wait for some load event. I'd imagine that you do. There is also this library https://howlerjs.com/. – lovelikelando Feb 14 '19 at 21:24
  • @garrettmaring no it's available as after loading it in a different window, the code works just fine. – Sourabh Feb 14 '19 at 21:25
  • @garrettmaring also, as it's just a small beep sound, I don't want to use a library. – Sourabh Feb 14 '19 at 21:29
  • If it's a periodic waveform that plays for a short time, you could probably use an [`OscillatorNode`](https://developer.mozilla.org/en-US/docs/Web/API/OscillatorNode) to produce the sound so you don't have to load network resources. I don't believe this will resolve the [autoplay policy](https://developer.mozilla.org/en-US/docs/Web/Media/Autoplay_guide#Autoplay_using_the_Web_Audio_API) issue though. – Patrick Roberts Feb 14 '19 at 21:36
  • Regarding your edit, I knew of that restriction on iOS, but was unaware of its implementation on any other OS/browser. What OS are you testing on? – James T Feb 14 '19 at 22:09
  • I've seen it now, have you read about the [PWA rule](https://developers.google.com/web/updates/2019/02/chrome-73-media-updates) (at the very bottom)? – James T Feb 14 '19 at 22:26
  • In the opening Summary of the [Chromium documentation](https://sites.google.com/a/chromium.org/dev/audio-video/autoplay), in regards to audio, it states that: **"Under the new policy media content will be allowed to autoplay. . . [if] the user tapped or clicked somewhere on the site during the browsing session"**. – James T Feb 14 '19 at 22:53
  • @JamesT I am using Mac and Chrome as the web browser. Yes, I have now added to the click event of body element and it's working. – Sourabh Feb 14 '19 at 23:21
  • I included the link and quote as you asked, "can anyone add what other interactions can be used to trigger this?", but according to the doc there aren't any others. – James T Feb 14 '19 at 23:41
  • @JamesT oh yes, thanks. – Sourabh Feb 15 '19 at 22:11

2 Answers2

0

You can try loading the audio when the document is ready and then play it later only if the resource is loaded (for this check you can register a callback on onloadeddata). Otherwise, if resource is not loaded, you can try loading it again.

$(document).ready(function()
{
    let aud = new Audio('https://dl.dropboxusercontent.com/s/1cdwpm3gca9mlo0/kick.mp3');
    let canPlay = false;

    aud.onloadeddata = () => (console.log("audio loaded"), canPlay = true);

    setInterval(function()
    {
       if (canPlay)
           aud.play();
       else
           aud = new Audio('https://dl.dropboxusercontent.com/s/1cdwpm3gca9mlo0/kick.mp3');
    }, 3000);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Shidersz
  • 16,846
  • 2
  • 23
  • 48
-1

Best solution I could come up with when I tried the same was:

const playPromise = audio.play();
if (playPromise !== null){
    playPromise.catch(function() { audio.play(); })
}

But sometimes (One out of ten) the second audio.play() where also uncaught and the audio did not play either. I suggest you made a loop that stops only when the Promise is finally caught.

Sood
  • 89
  • 7
  • Well, the problem I am not just playing the sound. I am poping open a chat window when that happens as well. – Sourabh Feb 14 '19 at 21:24
  • I'm not sure what the problem is here. Javascript is asynchronous, just pop the chat window and try play the sound. The main feature is clearly to open the dialog, so there is no matter if the song is played a quarter second later. – Sood Feb 14 '19 at 21:41
  • It's not just that. I had to find out why exactly is it behaving like that. – Sourabh Feb 14 '19 at 21:54