Skip to content

Commit

Permalink
added more general handling of audio context resumption, see #39
Browse files Browse the repository at this point in the history
  • Loading branch information
jbphet committed Oct 4, 2019
1 parent 13d82c5 commit 2ae3eaa
Showing 1 changed file with 34 additions and 35 deletions.
69 changes: 34 additions & 35 deletions js/Sound.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,9 @@ define( require => {
// modules
const Display = require( 'SCENERY/display/Display' );
const inherit = require( 'PHET_CORE/inherit' );
const platform = require( 'PHET_CORE/platform' );
const Property = require( 'AXON/Property' );
const vibe = require( 'VIBE/vibe' );

// sounds
const empty = require( 'sound!VIBE/empty.mp3' );

// global property that allows all audio to be turned on/off, see #11
const audioEnabledProperty = new Property( true );

Expand Down Expand Up @@ -189,46 +185,49 @@ define( require => {
audioEnabledProperty: audioEnabledProperty
} );

// Below is some platform-specific code for handling a number of issues related to audio. It may be possible to
// remove some or all of this as Web Audio becomes more consistently implemented.
// If an audio context was created, it means that we are using Web Audio. Many browsers have adopted policies to
// prevent a web page from being able to play a sound before a user interacts with it, so the following code was
// necessary to essentially detect when the user starts interacting with the sim and enable the audio context, which
// in turn enables the ability to produce sound.
if ( audioContext ) {

if ( !platform.mobileSafari ) {
// function to remove the listeners, used to avoid code duplication
const removeUserInteractionListeners = () => {
window.removeEventListener( 'touchstart', resumeAudioContext, false );
if ( Display.userGestureEmitter.hasListener( resumeAudioContext ) ) {
Display.userGestureEmitter.removeListener( resumeAudioContext );
}
};

// listener that resumes the audio context
const resumeAudioContext = () => {

// In some browsers the audio context is not allowed to run before the user interacts with the simulation. The
// motivation for this is to prevent auto-play of sound (mostly videos) when users land on websites, but it ends
// up preventing PhET sims from being able to play sound. To deal with this, we add a listener that can check the
// state of the audio context and "resume" it if necessary when the user starts interacting with the sim. See
// https://github.com/phetsims/vibe/issues/32 for more information.
if ( audioContext.state !== 'running' ) {

Display.userGestureEmitter.addListener( function resumeAudioContext() {
if ( audioContext.state !== 'running' ) {
// tell the audio context to resume
audioContext.resume()
.then( () => {
removeUserInteractionListeners();
} )
.catch( err => {
const errorMessage = 'error when trying to resume audio context, err = ' + err;
console.error( errorMessage );
assert && alert( errorMessage );
} );
}
else {

// the audio context isn't running, so tell it to resume
audioContext.resume().catch( function( err ) {
assert && assert( false, 'error when trying to resume audio context, err = ' + err );
} );
}
Display.userGestureEmitter.removeListener( resumeAudioContext ); // only do this once
} );
// audio context is already running, no need to listen anymore
removeUserInteractionListeners();
}
}
else {
};

// There is a different issue for audio on iOS+Safari: On this platform, we must play an audio file from a thread
// initiated by a user event such as touchstart before any sounds will play. This requires the user to touch the
// screen before audio can be played. See
// http://stackoverflow.com/questions/12517000/no-sound-on-ios-6-web-audio-api
const silence = new Sound( empty );
var playSilence = function() {
silence.play();
window.removeEventListener( 'touchstart', playSilence, false );
};
window.addEventListener( 'touchstart', playSilence, false );
}
// listen for a touchstart - this only works to resume the audio context on iOS devices (as of this writing)
window.addEventListener( 'touchstart', resumeAudioContext, false );

// listen for other user gesture events
Display.userGestureEmitter.addListener( resumeAudioContext );
}

return Sound;

} );

0 comments on commit 2ae3eaa

Please sign in to comment.