20

Is there a global way to detect when audio is playing or starts playing in the browser.

something like along the idea of if(window.mediaPlaying()){...

without having the code tied to a specific element?

EDIT: What's important here is to be able to detect ANY audio no matter where the audio comes from. Whether it comes from an iframe, a video, the Web Audio API, etc.

brillout
  • 7,804
  • 11
  • 72
  • 84
Chris
  • 2,166
  • 1
  • 24
  • 37

4 Answers4

14

No one should use this but it works.

Basically the only way that I found to access the entire window's audio is using MediaDevices.getDisplayMedia().

From there a MediaStream can be fed into an AnalyserNode that can be used to check the if the audio volume is greater than zero.

Only works in Chrome and maybe Edge (Only tested in Chrome 80 on Linux)

JSFiddle with <video>, <audio> and YouTube!

Important bits of code (cannot post in a working snippet because of the Feature Policies on the snippet iframe):

var audioCtx = new AudioContext();
var analyser = audioCtx.createAnalyser();

var bufferLength = analyser.fftSize;
var dataArray = new Float32Array(bufferLength);

window.isAudioPlaying = () => {
  analyser.getFloatTimeDomainData(dataArray);
  for (var i = 0; i < bufferLength; i++) {
    if (dataArray[i] != 0) return true;

  }
  return false;
}

navigator.mediaDevices.getDisplayMedia({
     video: true,
     audio: true
   })
   .then(stream => {
      if (stream.getAudioTracks().length > 0) {
        var source = audioCtx.createMediaStreamSource(stream);
        source.connect(analyser);

        document.body.classList.add('ready');
      } else {
        console.log('Failed to get stream. Audio not shared or browser not supported');
      }

   }).catch(err => console.log("Unable to open capture: ", err));
PaulMest
  • 12,925
  • 7
  • 53
  • 50
Trobol
  • 1,210
  • 9
  • 12
  • Good solution but unfortunately, this doesn't always work. I've tried it on Google Meet to detect if somebody is talking but the message says that audio is playing even when nobody speaks. – Aurasphere May 23 '21 at 09:18
  • 2
    There seems to a little bit of static coming from the tab, even if everyone is muted. I made an updated fiddle that shows a graph of the audio. [jsfiddle](https://jsfiddle.net/Thornton_Fernbacher/u78pce9g/62/) – Trobol May 23 '21 at 20:51
3

I read all MDN docs about Web Audio API but I didn't find any global flag on window that shows audio playing. But I have found a tricky way that shows ANY audio playing, no matter an iframe or video but about Web Audio API:

const allAudio = Array.from( document.querySelectorAll('audio') );
const allVideo = Array.from( document.querySelectorAll('video') );
const isPlaying = [...allAudio, ...allVideo].some(item => !item.paused);

Now, by the isPlaying flag we can detect if any audio or video is playing in the browser.

PaulMest
  • 12,925
  • 7
  • 53
  • 50
AmerllicA
  • 29,059
  • 15
  • 130
  • 154
  • 1
    Nearly positive this relies on the audio / video elements to be part of the DOM. I'm pretty sure you can create an audio element with JS & play it very easily without ever adding it to the DOM; so this wouldn't work for that scenario. – Lovethenakedgun Dec 13 '20 at 11:50
  • @Lovethenakedgun How did you manage to get around the "trustedTypes" error when doing that? I'm guessing the element is NOT injected into the DOM? Is that in the cloud or on a physical machine with a sound card+mic? Are you using python to do that or JS... – Gary Vaughan Jr May 25 '22 at 17:24
  • 1
    @GaryVaughanJr On https://file-examples.com, I got a link to their smallest example MP3 & ran in the console of another page on their site: `var aud = new Audio("https://file-examples.com//file_example_MP3_700KB.mp3"); aud.play();` & the MP3 track started playing. `new Audio` creates an ` – Lovethenakedgun Jun 05 '22 at 08:50
2

There is a playbackState property (https://developer.mozilla.org/en-US/docs/Web/API/MediaSession/playbackState), but not all browsers support it.

if(navigator.mediaSession.playbackState === "playing"){...
NinaW
  • 638
  • 3
  • 7
  • 1
    "without having the code tied to a specific element" is a crucial aspect of the question. (At least for me.) – brillout Mar 15 '20 at 10:50
  • True, in this case the `playbackState` should be set for the element first (default is `"none"`), but it can be set for any media source. – NinaW Mar 15 '20 at 15:53
  • If you have no control over the source element, you could follow Ed's logic and look for media elements in your document (audio, video, iframe) and check if any of them are playing audio. – NinaW Mar 15 '20 at 16:13
  • I'll have to spend some time looking into this. There are some options for accessing iframe's document and its internal DOM like `contentWindow` (https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement/contentWindow) and `frames` (https://developer.mozilla.org/en-US/docs/Web/API/Window/frames). I also found a related question at https://stackoverflow.com/questions/43745161/stop-all-audio-inside-iframes-from-parent that might be helpful. – NinaW Mar 15 '20 at 18:48
0

I was looking for a solution in Google, but i didn't find anything yet. Maybe you could check some data that has X value only when audio is playing. If you have some button that start playing the audio file, maybe you can be sure that the audio is playing by adding some event listener on the rep. button...

Maybe something like adding an event listener to the "audio" tag? If i remember correctly, audio tag has a "paused" attribute... And now i just remember that the audio has "paused" attribute...

Also, you may want to check this topic HTML5 check if audio is playing?

i jus find it five seconds ago jaja

Community
  • 1
  • 1
Emilio
  • 9
  • 1