123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837 |
- "use strict";
- /// vwf/model/sound.js is a sound driver that wraps the capabilities of the
- /// HTML5 web audio API.
- ///
- /// @module vwf/model/sound
- /// @requires vwf/model
- // References:
- // http://www.html5rocks.com/en/tutorials/webaudio/intro/
- // https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html
- // http://www.html5rocks.com/en/tutorials/webaudio/fieldrunners/
- define( [ "module", "vwf/model" ], function( module, model ) {
- // TODO: should these be stored in this.state so that the view can access them?
- var context;
- var soundData = {};
- var soundGroups = {};
- var masterVolume = 1.0;
- var logger;
- var soundDriver;
- var startTime = Date.now();
- var driver = model.load( module, {
- initialize: function() {
- // In case somebody tries to reference it before we get a chance to create it.
- // (it's created in the view)
- this.state.soundManager = {};
- soundDriver = this;
- logger = this.logger;
- try {
- // I quote: "For WebKit- and Blink-based browsers, you
- // currently need to use the webkit prefix, i.e.
- // webkitAudioContext."
- // http://www.html5rocks.com/en/tutorials/webaudio/intro/
- if ( !window.AudioContext ) {
- window.AudioContext = window.webkitAudioContext;
- }
- context = new AudioContext();
- }
- catch( e ) {
- // alert( 'Web Audio API is not supported in this browser' );
- logger.warnx( "initialize", "Web Audio API is not supported in this browser." );
- }
- },
- callingMethod: function( nodeID, methodName, params ) {
- if ( nodeID !== this.state.soundManager.nodeID ) {
- return undefined;
- }
- if ( !context ) {
- return undefined;
- }
- // variables that we'll need in the switch statement below. These all have function
- // scope, might as well declare them here.
- var soundDefinition, successCallback, failureCallback, exitCallback;
- var soundName, soundNames, soundDatum, soundDefinition, soundInstance;
- var instanceIDs, instanceID, i, volume, fadeTime, fadeMethod, instanceHandle;
- var soundGroup, groupName;
- switch( methodName ) {
- // arguments: soundDefinition, successCallback, failureCallback
- case "loadSound":
- soundDefinition = params[ 0 ];
- successCallback = params[ 1 ];
- failureCallback = params[ 2 ];
- if ( soundDefinition === undefined ) {
- logger.errorx( "loadSound", "The 'loadSound' method requires " +
- "a definition for the sound." );
- return undefined;
- }
- soundName = soundDefinition.soundName;
- if ( soundName === undefined ) {
- logger.errorx( "loadSound", "The sound definition must contain soundName." );
- return undefined;
- }
- if ( soundData[ soundName ] !== undefined ) {
- logger.errorx( "loadSound", "Duplicate sound named '" + soundName + "'." );
- return undefined;
- }
- soundData[ soundName ] = new SoundDatum( soundDefinition,
- successCallback,
- failureCallback );
- return;
- // arguments: soundName
- // returns: true if sound is done loading and is playable
- case "isReady":
- soundDatum = getSoundDatum( params[ 0 ] );
- return soundDatum !== undefined ? !!soundDatum.buffer : false;
- // arguments: soundName, exitCallback (which is called when the sound stops)
- // returns: an instance handle, which is an object:
- // { soundName: value, instanceID: value }
- // instanceID is -1 on failure
- case "playSound":
- soundName = params[ 0 ];
- soundDatum = getSoundDatum( soundName );
- exitCallback = params[ 1 ];
- return soundDatum ? soundDatum.playSound( exitCallback )
- : { soundName: soundName, instanceID: -1 };
- case "playSequence":
- soundNames = params;
- soundDatum = getSoundDatum( soundNames[ 0 ] );
- var playNext = function ( soundNames, current ){
- if (current !== soundNames.length){
- soundDatum = getSoundDatum( soundNames[ current ] );
- soundDatum.playSound( playNext ( soundNames, current + 1 ) );
- }
- }
- return soundDatum ? soundDatum.playSound( playNext ( soundNames, 0 ) )
- : { soundName: soundName, instanceID: - 1 };
- // arguments: soundName
- // returns: true if sound is currently playing
- case "isSoundPlaying":
- soundDatum = getSoundDatum( params[ 0 ] );
- return soundDatum ? soundDatum.isPlaying() : false;
- // arguments: instanceHandle
- // returns: true if sound is currently playing
- case "isInstancePlaying":
- return getSoundInstance( params[ 0 ] ) !== undefined;
- // // arguments: instanceHandle, volume, fadeTime, fadeMethod
- case "setVolume":
- instanceHandle = params [ 0 ];
- soundDatum = getSoundDatum( instanceHandle.soundName );
- if ( soundDatum ){
- soundDatum.setVolume ( params [ 0 ], params [ 1 ], params [ 2 ], params [ 3 ] );
- }
-
- // // arguments: volume (0.0-1.0)
- case "setMasterVolume":
- masterVolume = params [ 0 ];
- for ( soundName in soundData ){
- soundDatum = soundData[ soundName ];
- if ( soundDatum ) {
- soundDatum.resetOnMasterVolumeChange();
- }
- }
- // // arguments: instanceHandle
- case "hasSubtitle":
- instanceHandle = params [ 0 ];
- soundDatum = getSoundDatum( instanceHandle.soundName );
- return soundDatum ? !!soundDatum.subtitle : undefined;
- // // arguments: instanceHandle
- case "getSubtitle":
- instanceHandle = params [ 0 ];
- soundDatum = getSoundDatum( instanceHandle.soundName );
- return soundDatum ? soundDatum.subtitle : undefined;
- // arguments: instanceHandle
- // returns: the duration of the sound
- case "getDuration":
- instanceHandle = params[ 0 ];
- soundDatum = getSoundDatum( instanceHandle.soundName );
- return soundDatum && soundDatum.buffer ? soundDatum.buffer.duration : undefined;
- // arguments: instanceHandle
- case "stopSoundInstance":
- instanceHandle = params[ 0 ];
- //If a user chooses to pass just a soundName, stop all instances with that name.
- if ( !instanceHandle.soundName ){
- soundName = params[ 0 ];
- soundDatum = getSoundDatum( soundName );
- soundDatum && soundDatum.stopDatumSoundInstances();
- } else {
- //Otherwise stop the specific instance.
- soundDatum = getSoundDatum( instanceHandle.soundName );
- soundDatum && soundDatum.stopInstance( instanceHandle );
- }
- return;
- // arguments: groupName
- case "stopSoundGroup":
- groupName = params[ 0 ];
- soundGroup = soundGroups[ groupName ];
- soundGroup && soundGroup.clearQueue();
- soundGroup && soundGroup.stopPlayingSound();
- return;
- // arguments: none
- case "stopAllSoundInstances":
- for ( groupName in soundGroups ) {
- soundGroup = soundGroups[ groupName ];
- soundGroup && soundGroup.clearQueue();
- }
- for ( soundName in soundData ) {
- soundDatum = soundData[ soundName ];
- soundDatum && soundDatum.stopDatumSoundInstances();
- }
- return undefined;
- // arguments: soundName
- case "getSoundDefinition":
- soundDatum = getSoundDatum( params[ 0 ] );
- return soundDatum ? soundDatum.soundDefinition : undefined;
-
- }
- return undefined;
- }
- } );
- function SoundDatum( soundDefinition, successCallback, failureCallback ) {
- this.initialize( soundDefinition, successCallback, failureCallback );
- return this;
- }
- SoundDatum.prototype = {
- constructor: SoundDatum,
- // the name
- name: "",
- // the actual sound
- buffer: null,
- // a hashtable of sound instances
- playingInstances: null,
- // control parameters
- initialVolume: 1.0,
- isLooping: false,
- allowMultiplay: false,
- soundDefinition: null,
- playOnLoad: false,
- subtitle: undefined,
- soundGroup: undefined,
- groupReplacementMethod: undefined,
- queueDelayTime: undefined, // in seconds
- // a counter for creating instance IDs
- instanceIDCounter: 0,
- initialize: function( soundDefinition, successCallback, failureCallback ) {
- this.name = soundDefinition.soundName;
- this.playingInstances = {};
- this.soundDefinition = soundDefinition;
- if ( this.soundDefinition.isLooping !== undefined ) {
- this.isLooping = soundDefinition.isLooping;
- }
- if ( this.soundDefinition.allowMultiplay !== undefined ) {
- this.allowMultiplay = soundDefinition.allowMultiplay;
- }
- if (this.soundDefinition.initialVolume !== undefined ) {
- this.initialVolume = soundDefinition.initialVolume;
- }
- if ( this.soundDefinition.playOnLoad !== undefined ) {
- this.playOnLoad = soundDefinition.playOnLoad;
- }
- this.subtitle = this.soundDefinition.subtitle;
- var soundGroupName = this.soundDefinition.soundGroup;
- if ( soundGroupName ) {
- if ( !soundGroups[ soundGroupName ] ) {
- soundGroups[ soundGroupName ] =
- new SoundGroup( soundGroupName );
- }
- this.soundGroup = soundGroups[ soundGroupName ];
- }
- this.groupReplacementMethod = this.soundDefinition.groupReplacementMethod;
- if ( this.groupReplacementMethod && !this.soundGroup ) {
- logger.warnx( "SoundDatum.initialize",
- "You defined a replacement method but not a sound " +
- "group. Replacement is only done when you replace " +
- "another sound in the same group!" );
- }
- if ( this.soundDefinition.queueDelayTime !== undefined ) {
- this.queueDelayTime = this.soundDefinition.queueDelayTime;
- if ( this.groupReplacementMethod !== "queue" ) {
- logger.warnx( "SoundDatum.initialize",
- "You defined a queue delay time, but " +
- "the replacement method is not 'queue'.");
- }
- } else {
- this.queueDelayTime =
- this.groupReplacementMethod === "queue" ? 0.8 : 0;
- }
- // Create & send the request to load the sound asynchronously
- var request = new XMLHttpRequest();
- request.open( 'GET', soundDefinition.soundURL, true );
- request.responseType = 'arraybuffer';
- var thisSoundDatum = this;
- request.onload = function() {
- context.decodeAudioData(
- request.response,
- function( buffer ) {
- thisSoundDatum.buffer = buffer;
- if ( thisSoundDatum.playOnLoad === true ) {
- thisSoundDatum.playSound( null, true );
- }
- successCallback && successCallback();
- },
- function() {
- logger.warnx( "SoundDatum.initialize", "Failed to load sound: '" +
- thisSoundDatum.name + "'." );
- delete soundData[ thisSoundDatum.name ];
- failureCallback && failureCallback();
- }
- );
- }
- request.send();
- },
- playSound: function( exitCallback ) {
- if ( !this.buffer ) {
- logger.errorx( "SoundDatum.playSound", "Sound '" + this.name + "' hasn't finished " +
- "loading, or loaded improperly." );
- return { soundName: this.name, instanceID: -1 };
- }
- if ( !this.allowMultiplay && this.isPlaying() ) {
- return { soundName: this.name,
- instanceID: this.playingInstances[ 0 ] };
- }
- var id = this.instanceIDCounter;
- ++this.instanceIDCounter;
- this.playingInstances[ id ] = new PlayingInstance( this, id, exitCallback );
- return { soundName: this.name, instanceID: id };
- },
- stopInstance: function( instanceHandle ) {
- var soundInstance = this.playingInstances[ instanceHandle.instanceID ];
- soundInstance && soundInstance.stop();
- },
- stopDatumSoundInstances: function () {
- for ( var instanceID in this.playingInstances ) {
- var soundInstance = this.playingInstances[ instanceID ];
- soundInstance && soundInstance.stop();
- }
- // I have no freaking idea why uncommenting this breaks absolutely
- // everything, but it does!
- // this.playingInstances = {};
- },
- resetOnMasterVolumeChange: function () {
- for ( var instanceID in this.playingInstances ) {
- var soundInstance = this.playingInstances[ instanceID ];
- if ( soundInstance ) {
- soundInstance.resetVolume();
- }
- }
- },
- setVolume: function ( instanceHandle, volume, fadeTime, fadeMethod ) {
- // arguments: instanceHandle, volume, fadeTime, fadeMethod
- var soundInstance = getSoundInstance( instanceHandle );
- soundInstance && soundInstance.setVolume( volume, fadeTime, fadeMethod );
- },
- isPlaying: function() {
- var instanceIDs = Object.keys( this.playingInstances );
- return instanceIDs.length > 0;
- },
- }
- function PlayingInstance( soundDatum, id, exitCallback, successCallback ) {
- this.initialize( soundDatum, id, exitCallback, successCallback );
- return this;
- }
- PlayingInstance.prototype = {
- constructor: PlayingInstance,
- // our id, for future reference
- id: undefined,
- // a reference back to the soundDatum
- soundDatum: null,
- // the control nodes for the flowgraph
- sourceNode: undefined,
- gainNode: undefined,
- // we need to know the volume for this node *before* the master volume
- // adjustment is applied. This is that value.
- localVolume$: undefined,
- // stopped, delayed, playing, stopping, delayCancelled
- playStatus: undefined,
- initialize: function( soundDatum, id, exitCallback, successCallback ) {
- // NOTE: from http://www.html5rocks.com/en/tutorials/webaudio/intro/:
- //
- // An important point to note is that on iOS, Apple currently mutes all sound
- // output until the first time a sound is played during a user interaction
- // event - for example, calling playSound() inside a touch event handler.
- // You may struggle with Web Audio on iOS "not working" unless you circumvent
- // this - in order to avoid problems like this, just play a sound (it can even
- // be muted by connecting to a Gain Node with zero gain) inside an early UI
- // event - e.g. "touch here to play".
- this.id = id;
- this.soundDatum = soundDatum;
- this.playStatus = "stopped";
- this.sourceNode = context.createBufferSource();
- this.sourceNode.buffer = this.soundDatum.buffer;
- this.sourceNode.loop = this.soundDatum.isLooping;
- this.localVolume$ = this.soundDatum.initialVolume;
- this.gainNode = context.createGain();
- this.sourceNode.connect( this.gainNode );
- this.gainNode.connect( context.destination );
- this.resetVolume();
- var group = soundDatum.soundGroup;
- // Browsers will handle onended differently depending on audio
- // filetype - needs support.
- var thisInstance = this;
- this.sourceNode.onended = function() {
- thisInstance.playStatus = "stopped";
- fireSoundEvent( "soundFinished", thisInstance );
- // logger.logx( "PlayingInstance.onended",
- // "Sound ended: '" + thisInstance.soundDatum.name +
- // "', Timestamp: " + timestamp() );
- if ( group ) {
- group.soundFinished( thisInstance );
- var nextInstance = group.unQueueSound();
- if ( nextInstance ) {
- var delaySeconds = nextInstance.soundDatum.queueDelayTime;
- // logger.logx( "PlayingInstance.onended",
- // "Popped from the queue: '" +
- // nextInstance.soundDatum.name +
- // ", Timeout: " + delaySeconds +
- // ", Timestamp: " + timestamp() );
- if ( delaySeconds > 0 ) {
- nextInstance.startDelayed( delaySeconds );
- } else {
- nextInstance.start();
- }
- }
- }
- delete soundDatum.playingInstances[ id ];
- exitCallback && exitCallback();
- }
- if ( group ) {
- switch ( soundDatum.groupReplacementMethod ) {
- case "queue":
- // We're only going to play the sound if there isn't
- // already a sound from this group playing.
- // Otherwise, add it to a queue to play later.
- if ( group.getPlayingSound() ) {
- group.queueSound( this );
- } else {
- this.start();
- }
- break;
- case "replace":
- group.clearQueue();
- group.stopPlayingSound();
- this.start();
- break;
- default:
- logger.errorx( "PlayingInstance.initialize",
- "This sound ('" +
- thisInstance.soundDatum.name +
- "') is in a group, but doesn't " +
- "have a valid replacement method!" );
- group.clearQueue();
- group.stopPlayingSound();
- this.start();
- }
- } else {
- this.start();
- }
- },
- getVolume: function() {
- return this.localVolume$ * ( masterVolume !== undefined ? masterVolume : 1.0 );
- },
- setVolume: function( volume, fadeTime, fadeMethod ) {
- if ( !volume ) {
- logger.errorx( "PlayingInstance.setVolume", "The 'setVolume' " +
- "method requires a volume." );
- return;
- }
- this.localVolume$ = volume;
- if ( !fadeTime || ( fadeTime <= 0 ) ) {
- fadeMethod = "immediate";
- }
- var now = context.currentTime;
- this.gainNode.gain.cancelScheduledValues( now );
- switch( fadeMethod ) {
- case "linear":
- var endTime = now + fadeTime;
- this.gainNode.gain.linearRampToValueAtTime( this.getVolume(),
- endTime );
- break;
- case "exponential":
- case undefined:
- this.gainNode.gain.setTargetValueAtTime( this.getVolume(),
- now, fadeTime );
- break;
- case "immediate":
- this.gainNode.gain.value = this.getVolume();
- break;
- default:
- logger.errorx( "PlayingInstance.setVolume", "Unknown fade method: '" +
- fadeMethod + "'. Using an exponential " +
- "fade." );
- this.gainNode.gain.setTargetValueAtTime( this.getVolume(),
- now, fadeTime );
- }
- },
- resetVolume: function() {
- this.setVolume(this.localVolume$);
- },
- start: function() {
- switch ( this.playStatus ) {
- case "playing":
- logger.warnx( "PlayingInstance.start",
- "Duplicate call to start. Sound: '" +
- this.soundDatum.name + "'." );
- break;
- case "stopping":
- logger.warnx( "PlayingInstance.start", "Start is being " +
- "called, but we're not done stopping yet. " +
- "Is that bad?" );
- // deliberately drop through - we can restart it.
- case "delayed":
- case "stopped":
- var group = this.soundDatum.soundGroup;
- var playingSound = group ? group.getPlayingSound()
- : undefined;
- if ( !group ||
- !playingSound ||
- ( ( playingSound === this ) &&
- ( this.playStatus !== "stopping" ) ) ) {
- this.playStatus = "playing";
- group && group.setPlayingSound( this );
- this.sourceNode.start( 0 );
- // logger.logx( "startSoundInstance",
- // "Sound started: '" + this.soundDatum.name +
- // "', Timestamp: " + timestamp() );
- fireSoundEvent( "soundStarted", this );
- } else {
- if ( ( playingSound !== this ) &&
- ( playingSound.playStatus != "stopping" ) ) {
- logger.errorx( "PlayingInstance.start",
- "We are trying to start a sound " +
- "('" + this.soundDatum.name +
- "') that is in a sound group, " +
- "but the currently playing sound " +
- "in that group ('" +
- playingSound.soundDatum.name +
- "') isn't in the process of " +
- "stopping. This is probably bad." );
- }
- // Because the sound API is asynchronous, this happens
- // fairly often. The trick is to just stuff this
- // sound onto the front of the queue, and let it run
- // whenever whatever is playing right now finishes.
- group.jumpQueue( this );
- }
- break;
- case "delayCancelled":
- // don't start - we've been trumped.
- // NOTE: it's theoretically possible to re-queue the sound
- // in between when the delay is cancelled and when it
- // finishes the delay and calls start. In this case the
- // sound might be delayed more than desired, but should
- // still eventually play (I think). If you're restarting
- // sounds alot, consider looking into this.
- this.playStatus = "stopped";
- break;
- default:
- logger.errorx( "PlayingInstance.start", "Invalid " +
- "playStatus: '" + this.playStatus + "'!" );
- }
- },
- startDelayed: function( delaySeconds ) {
- var group = this.soundDatum.soundGroup;
- if ( group ) {
- if ( group.getPlayingSound() ) {
- logger.errorx( "PlayingInstance.startDelayed",
- "How is there already a sound playing " +
- "when startDelayed() is called?" );
- return;
- }
- group.setPlayingSound( this );
- }
- this.playStatus = "delayed";
- setTimeout( this.start.bind(this), delaySeconds * 1000 );
- },
- stop: function() {
- switch ( this.playStatus ) {
- case "playing":
- this.playStatus = "stopping";
- this.sourceNode.stop();
- break;
- case "delayed":
- this.playStatus = "delayCancelled";
- var group = this.soundDatum.soundGroup;
- if ( group ) {
- group.soundFinished( this );
- }
- break;
- case "delayCancelled":
- case "stopping":
- case "stopped":
- logger.warnx( "PlayingInstance.stop", "Duplicate call " +
- "to stop (or it was never started). " +
- "Sound: '" + this.soundDatum.name + "'." );
- break;
- default:
- logger.errorx( "PlayingInstance.stop", "Invalid " +
- "playStatus: '" + this.playStatus + "'!" );
- }
- },
- }
- function SoundGroup( groupName ) {
- this.initialize( groupName );
- return this;
- }
- SoundGroup.prototype = {
- constructor: SoundGroup,
- // Trying out a new convention - make the values in the prototype be
- // something obvious, so I can tell if they don't get reset.
- name$: "PROTOTYPE", // the name of the group, for debugging
- queue$: "PROTOTYPE", // for storing queued sounds while they wait
- playingSound$: "PROTOTYPE", // the sound that is currently playing
- initialize: function( groupName ) {
- this.name$ = groupName;
- this.queue$ = [];
- this.playingSound$ = undefined;
- },
- getPlayingSound: function() {
- return this.playingSound$;
- },
- setPlayingSound: function( playingInstance ) {
- if ( this.playingSound$ ) {
- if ( this.playingSound$ !== playingInstance ) {
- logger.errorx( "SoundGroup.setPlayingSound",
- "Trying to set playingSound to '" +
- playingInstance.soundDatum.name +
- "', but it is already set to '" +
- this.playingSound$.soundDatum.name + "'!");
- } else if ( !this.playingSound$.playStatus !== "delayed" ) {
- logger.errorx("SoundGroup.setPlayingSound",
- "How are we re-setting the playing sound " +
- "when we're not in a delay? Sound: '" +
- this.playingSound$.soundDatum.name + "'." );
- }
- } else {
- this.playingSound$ = playingInstance;
- }
- },
- soundFinished: function( playingInstance ) {
- if ( playingInstance !== this.playingSound$ ) {
- logger.errorx( "SoundGroup.soundFinished", "'" +
- playingInstance.soundDatum.name + "' just " +
- "repored that it is finished, but we thought " +
- "that '" + this.playingSound$.soundDatum.name +
- "' was playing!");
- return;
- }
- this.playingSound$ = undefined;
- },
- stopPlayingSound: function() {
- this.playingSound$ && this.playingSound$.stop();
- },
- // get in the back of the queue of sounds to play
- queueSound: function( playingInstance ) {
- this.queue$.unshift( playingInstance );
- },
- // jump to the front of the queue of sounds to play
- jumpQueue: function( playingInstance ) {
- this.queue$.push( playingInstance );
- },
- unQueueSound: function() {
- return this.queue$.pop();
- },
- clearQueue: function() {
- this.queue$.length = 0;
- },
- hasQueuedSounds: function() {
- return queue$.length > 0;
- },
- }
- function getSoundDatum( soundName ) {
- if ( soundName === undefined ) {
- logger.errorx( "getSoundDatum", "The 'getSoundDatum' method " +
- "requires the sound name." );
- return undefined;
- }
- var soundDatum = soundData[ soundName ];
- if ( soundDatum === undefined ) {
- logger.errorx( "getSoundDatum", "Sound '" + soundName +
- "' not found." );
- return undefined;
- }
- return soundDatum;
- }
- function getSoundInstance( instanceHandle ) {
- if ( instanceHandle === undefined ) {
- logger.errorx( "getSoundInstance", "The 'GetSoundInstance' " +
- "method requires the instance ID." );
- return undefined;
- }
- if ( ( instanceHandle.soundName === undefined ) ||
- ( instanceHandle.instanceID === undefined ) ) {
- logger.errorx( "getSoundInstance", "The instance handle must " +
- "contain soundName and instanceID values");
- return undefined;
- }
- var soundDatum = getSoundDatum( instanceHandle.soundName );
- if ( soundDatum.isLayered === true ) {
- return soundDatum;
- } else {
- return soundDatum ? soundDatum.playingInstances[ instanceHandle.instanceID ]
- : undefined;
- }
- }
- function fireSoundEvent( eventString, instance ) {
- vwf_view.kernel.fireEvent( soundDriver.state.soundManager.nodeID,
- eventString,
- [ { soundName: instance.soundDatum.name,
- instanceID: instance.id } ] );
- }
- function timestamp() {
- var delta = Date.now() - startTime;
- var minutes = Math.floor( delta / 60000 );
- var seconds = ( delta % 60000 ) / 1000;
- return "" + minutes + ":" + seconds;
- }
- return driver;
- } );
|