WebAPI/AudioChannels

From MozillaWiki
Jump to navigation Jump to search

This API introduces the concept of a hierarchy of audio channels. The channels are prioritized as to allow "silencing all channels with priority lower than X".

The problems that we are trying to solve are:

  • When the user answers a phone call, the sound from all apps should be silenced
  • The alarm clock shouldn't be muted even if normal audio is muted. This to prevent the user oversleeping due to having muted the phone the previous day.
  • When the user leaves an app, under normal circumstances the app should be muted.
  • Some apps need to be able to opt in to not getting muted when the user leaves the app, such as the music player app or the radio app.
  • When the volume keys are used it should change the volume for different audio types depending on context. For example while in the alarm app, the volume keys should adjust the alarm volume and not the "normal" volume.

https://etherpad.mozilla.org/sound-stream-types

The channels are:

  • normal: UI sounds, web content, music, radio
  • notification: New email, incoming SMS
  • alarm: alarm clock, calendar alarms
  • telephony: phone calls, voip calls
  • publicnotification: forced camera shutter sounds

We'll have separate mute and volume settings per channel. We'll additionally have a volume and mute setting for a "headphones" channel.

For now all sounds are directed through headphones/headset when they are plugged in. We discussed possibly making the alarm sound through both headphones and speaker, but we deemed this a non-v1 feature.

For now all audio channels except "telephony" never use the built-in earpiece. I.e. they always use the speaker or headphones/headset. We might introduce using the built-in earpiece for "normal" sounds in a future version.

Application API

interface AudioChannelManager : EventTarget {
 muteBelow(DOMString type);
 unmuteAll();
 readonly attribute DOMString[] mutedChannels;
 attribute EventHandler onmutedchange;

 // We might not need this headphones section for v1. 
 readonly attribute boolean headphones;
 attribute EventHandler onheadphoneschange; // Always fired before audio start playing through the new channel

 attribute boolean telephonySpeaker; // When set to true, the "telephony" channel uses the speaker.
                                     // I.e. this is the same as navigator.telephony.speakerEnabled
                                     // Might not be needed for v1 if we aren't targetting VoIP apps.

 attribute DOMString volumeControlChannel; // The channel whose volume is changed if the user presses the
                                           // volumeup/down buttons. Defaults to "normal".
}

When muteBelow is called, it changes the mutedChannels property of all AudioChannelManager instances in all apps. Also all audio for all muted channels are muted. The audio's are not automatically paused. Instead applications are expected to listen to the "mutedchanged" event and call pause() as they see fit, for example if they want to resume music where it was when the music was muted.

Likewise, when when the user leaves an app, the mutedChannels property changes to include all channels.

If two or more apps call muteBelow, the mutedChannels property of all apps are set to the union of the muted channels.

Whenever the mutedChannels property is changed, the "mutedchange" event is fired. Note that it's only fired if the property actually changes. So if A first calls muteBelow("telephony") and then app B calls muteBelow("notification"), only the first call will result in "mutedchange" events being fired. If app A then calls unmuteAll() then the mutedChannels property changes to just include "normal" and the "mutedchange" event is fired again. Once app B calls unmuteAll() mutedChannels changes to an empty list and "mutedchange" is fired again.

For apps that want to keep being able to play audio when the user leaves the app, the app can call requestWakeLock("audio") to grab an "audio" wakelock. As long as the app holds the "audio" wakelock its mutedChannels property won't change due to the user leaving the app. The mutedChannels property can of course still change if another app calls muteBelow("...").

System and Browser API changes

We need to enable the system app to know any time an app sets the volumeControlChannel property. A simple solution would be to fire a mozChromeEvent which indicates which app set the property and what it set the property to. That way the system app can track what value the property has in all apps and so when the volume buttons are used, the system app can know which is the visible app and what the property is set to in that app.

In order to support a "audio" wakelock we should extend the browser API with two things. First off, we should add the ability to enumerate the set of locks that are being held by any page inside the <iframe mozbrowser>. As well as an event that is fired whenever that list changes.

Second, we should add an API to mute/unmute the contents of the <iframe mozbrowser>. When a <iframe mozbrowser> is muted or unmuted, we'd change the mutedChannels property on all channel managers inside the browser, and fire "mutedchange" event as needed.

Security model

In order to get access to anything more than the "normal" channel, the application needs to enumerate these channels in the permissions property in the app manifest. So something like:

permissions: {

 ...
 audio: {
   channels: ["notification", "alarm"]
 },
 ...

}

This would enable the app to play sound through both the "notification" channel and the "alarm" channel. As well as call muteBelow("notification") and muteBelow("alarm").

I'm not sure if we need to have prompts if non-privileged apps try to use channels beyond the "normal" channel.