@@ -22,6 +22,40 @@ This program based on:
2222Twine: HTML5 sound macros by Leon Arnott of Glorious Trainwrecks
2323the source and influence of which appear under a Creative Commons CC0 1.0 Universal License
2424
25+ This program uses
26+ * easeInOutQuad()
27+ * Copyright © 2001 Robert Penner
28+ * All rights reserved.
29+ *
30+ * As distributed by Kirupa
31+ * http://www.kirupa.com/forum/showthread.php?378287-Robert-Penner-s-Easing-Equations-in-Pure-JS-(no-jQuery)
32+ *
33+ * Open source under the BSD License.
34+ *
35+ *
36+ * Redistribution and use in source and binary forms, with or without modification,
37+ * are permitted provided that the following conditions are met:
38+ *
39+ * Redistributions of source code must retain the above copyright notice, this list of
40+ * conditions and the following disclaimer.
41+ * Redistributions in binary form must reproduce the above copyright notice, this list
42+ * of conditions and the following disclaimer in the documentation and/or other materials
43+ * provided with the distribution.
44+ *
45+ * Neither the name of the author nor the names of contributors may be used to endorse
46+ * or promote products derived from this software without specific prior written permission.
47+ *
48+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
49+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
50+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
51+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
52+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
53+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
54+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
55+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
56+ * OF THE POSSIBILITY OF SUCH DAMAGE.
57+
58+
2559This program is free software: you can redistribute it and/or modify
2660it under the terms of the GNU General Public License as published by
2761the Free Software Foundation, either version 3 of the License, or
@@ -55,6 +89,21 @@ GNU General Public License for more details.
5589 var loopLabel = "Loop?" ;
5690
5791
92+ //------------ Robert Penner via Kirupa math methods ----------
93+ //-------------------------------------------------------------
94+
95+ function easeInCubic ( currentIteration , startValue , changeInValue , totalIterations ) {
96+ return changeInValue * Math . pow ( currentIteration / totalIterations , 3 ) + startValue ;
97+ }
98+
99+ function easeOutCubic ( currentIteration , startValue , changeInValue , totalIterations ) {
100+ return changeInValue * ( Math . pow ( currentIteration / totalIterations - 1 , 3 ) + 1 ) + startValue ;
101+ }
102+
103+
104+ //------------ End Math methods -------------------------------
105+ //-------------------------------------------------------------
106+
58107 //------------- pausableTimeout ---------
59108 //--------------------------------------
60109 function pausableTimeout ( func , params ) {
@@ -152,33 +201,53 @@ GNU General Public License for more details.
152201 } ;
153202
154203
204+
155205 // Perform fade on specified audio
156206 //
157207 this . __fadeSound = function ( audioObj , fadeIn ) {
158208
159- var goalVolume = globalVolume * this . volumeProportion ;
160- var tempVolume = audioObj . volume ;
161- var increment = ( ( goalVolume * updateInterval ) / this . overlap ) * ( fadeIn ? 1 : - 1 ) ;
209+ // DELETE ME
210+ //fadeIn = false;
211+ //audioObj.volume = globalVolume * this.volumeProportion;
212+ // END DELETE BLOCK
213+
214+ var maxVolume = globalVolume * this . volumeProportion ;
215+ var startVolume = fadeIn ? 0 : globalVolume * this . volumeProportion ;
216+ var deltaVolume = globalVolume * this . volumeProportion * ( fadeIn ? 1 : - 1 ) ;
217+
218+ //alert("__fadeSound! fadeIn " + fadeIn + ", globalVolume " + globalVolume + ", volProp " + this.volumeProportion + " startVol " + startVolume + " deltaVolume " + deltaVolume);
219+
220+
221+ // Handy vars for easing
222+ var totalIterations = this . overlap / updateInterval ;
223+ var currentIteration = 1 ;
162224
163225 audioObj . interval = setInterval ( function ( ) {
164226
165227 // If you ever want to start/end at a volume other than zero, change goalVolume in the line below to be abs(goalVolume-startVolume) or some such
166228 //
167- tempVolume = Math . min ( goalVolume , Math . max ( 0 , tempVolume + increment ) ) ;
229+ // tempVolume = Math.min(goalVolume, Math.max(0, tempVolume + increment));
168230
169- //Easing (increment, startpoint, endpoint) chooses the next friendly value between the given min and max; prevents sound popping in or out
170- // was Math.easeInOut(tempVolume, 0, goalVolume)
171- audioObj . volume = ( ! fadeIn ) ? Math . easeInOut ( tempVolume , 0 , goalVolume ) : Math . easeInOut ( tempVolume , goalVolume , 0 ) ;
231+ //Use easing to prevent sound popping in or out
232+ //
233+ // easeInCubic(currentIteration, startValue, changeInValue, totalIterations)
234+ var desiredVolume = fadeIn ? easeInCubic ( currentIteration , startVolume , deltaVolume , totalIterations ) : easeOutCubic ( currentIteration , startVolume , deltaVolume , totalIterations ) ;
235+ //desiredVolume = Math.min(maxVolume, Math.max(0, desiredVolume)); // No more than goal, no less than zero
236+ //alert("desVol is " + desiredVolume + " because currentIter " + currentIteration + " and startVolume " + startVolume + ", deltaVolume " + deltaVolume + ", totalIterations " + totalIterations + " and bTW quittin' vol is " + (startVolume + deltaVolume));
237+ audioObj . volume = desiredVolume ;
238+ currentIteration += 1 ;
172239
173- if ( tempVolume === 0 || tempVolume === goalVolume ) clearInterval ( audioObj . interval ) ;
240+ if ( audioObj . volume === ( startVolume + deltaVolume ) ) {
241+ //alert("Grats! You reached your destination of " + audioObj.volume);
242+ clearInterval ( audioObj . interval ) ;
243+ }
174244
175245 //This effectively stops the loop and poises the volume to be played again
176246 //That way the clip isn't needlessly looping when no one can hear it.
177- if ( tempVolume === 0 ) {
247+ if ( audioObj . volume === 0 ) {
178248 audioObj . pause ( ) ;
179249 audioObj . currentTime = 0 ;
180- // This is usually redundant, as playsound() adjusts the volume before playing, but better safe than sorry.
181- audioObj . volume = goalVolume ;
250+ //audioObj.volume = goalVolume;
182251 }
183252 } , updateInterval ) ;
184253 } ;
@@ -199,24 +268,34 @@ GNU General Public License for more details.
199268 var nextAudioObj = sqAudioObj . alternate ? sqAudioObj . mainAudio : sqAudioObj . partnerAudio ;
200269 sqAudioObj . alternate = ! sqAudioObj . alternate ;
201270
202- // fade out current sound
203- //
204- sqAudioObj . __fadeSound ( currAudioObj , false ) ;
271+ // Don't even bother with crossfade if there's no overlap
272+ if ( sqAudioObj . overlap !== undefined && sqAudioObj . overlap > 1 ) {
205273
206- // And fade in our partner
207- //
208- nextAudioObj . currentTime = 0 ;
209- nextAudioObj . volume = 0 ;
210- nextAudioObj . play ( ) ;
211- sqAudioObj . __fadeSound ( nextAudioObj , true ) ;
274+ // fade out current sound
275+ //
276+ sqAudioObj . _fadeSound ( currAudioObj , false ) ;
277+
278+ // And fade in our partner
279+ //
280+ //nextAudioObj.volume = 0;
281+ //if (nextAudioObj.currentTime > 0) nextAudioObj.currentTime = 0;
282+ //nextAudioObj.play();
283+ sqAudioObj . _fadeSound ( nextAudioObj , true ) ;
284+
285+ }
286+ else {
287+ sqAudioObj . updateVolume ( ) ;
288+ nextAudioObj . currentTime = 0 ;
289+ nextAudioObj . play ( ) ;
290+ }
212291
213292 // Kick off the next timer to crossfade
214293 // Might as well garbage collect the old crossfadeTimeout, too.
215294 //
216- if ( sqAudioObj . crossfadeTimeout !== undefined ) { sqAudioObj . crossfadeTimeout . stopAndClear ( ) ; delete sqAudioObj . crossfadeTimeout ; }
217- if ( isNaN ( sqAudioObj . getDuration ( ) ) ) { this . error ( "Can't loop because duration is not known (audio not loaded, probably not found.)" ) ; return ; }
218- sqAudioObj . crossfadeTimeout = new pausableTimeout ( sqAudioObj . _crossfadeLoop , [ sqAudioObj , nextAudioObj ] ) ;
219- sqAudioObj . crossfadeTimeout . activate ( sqAudioObj . getDuration ( ) * 1000 - sqAudioObj . overlap ) ;
295+ // if (sqAudioObj.crossfadeTimeout !== undefined) { sqAudioObj.crossfadeTimeout.stopAndClear(); delete sqAudioObj.crossfadeTimeout; }
296+ // if (isNaN(sqAudioObj.getDuration())) { this.error("Can't loop because duration is not known (audio not loaded, probably not found.)"); return; }
297+ // sqAudioObj.crossfadeTimeout = new pausableTimeout(sqAudioObj._crossfadeLoop, [sqAudioObj, nextAudioObj]);
298+ // sqAudioObj.crossfadeTimeout.activate(sqAudioObj.getDuration()*1000-sqAudioObj.overlap);
220299
221300 } ;
222301
@@ -247,7 +326,10 @@ GNU General Public License for more details.
247326 // Fade sound on whatever the active audio is
248327 //
249328 this . fadeSound = function ( fadeIn ) {
250- if ( fadeIn ) this . stopAndClear ( ) ;
329+ if ( fadeIn ) {
330+ this . stopAndClear ( ) ;
331+ this . looping = true ;
332+ }
251333 else this . looping = false ;
252334 this . _fadeSound ( this . _getActiveAudio ( ) , fadeIn ) ;
253335 } ;
@@ -258,10 +340,10 @@ GNU General Public License for more details.
258340 this . volumeProportion = volumeProportion ;
259341 } ;
260342
261- // Update volume of both audio clips (assumes vol proportion and global vol already set)
343+ // Update volume of active audio clips (assumes vol proportion and global vol already set)
262344 //
263345 this . updateVolume = function ( ) {
264- this . mainAudio . volume = this . partnerAudio . volume = globalVolume * this . volumeProportion ;
346+ this . _getActiveAudio ( ) . volume = globalVolume * this . volumeProportion ;
265347 } ;
266348
267349 // Play the current audio object and reactivate any paused timer
@@ -379,35 +461,6 @@ GNU General Public License for more details.
379461 return clips [ clipName ] ;
380462 }
381463
382-
383- // Centralized function for fading multiple sounds
384- //
385- function loopSounds ( loopNameString , fadeIn , volumeProportion , overlap ) {
386-
387- // loopNameString will be an object like "some.mp3,this.mp3"
388- // Convert to a string and break into pieces
389- // Don't bother converting to audio clip at this point--
390- // the call to fadeSound() will take care of that
391- //
392- var loopNames = loopNameString . split ( "," ) ;
393- for ( var index in loopNames ) {
394- if ( loopNames . hasOwnProperty ( index ) ) {
395- var loopName = loopNames [ index ] ;
396- loopName = cleanClipName ( loopName ) ;
397-
398- if ( volumeProportion !== undefined ) getSoundTrack ( loopName ) . setVolumeProportion ( volumeProportion ) ;
399- if ( overlap !== undefined ) getSoundTrack ( loopName ) . overlap = overlap ;
400-
401- if ( fadeIn ) fadeSound ( getSoundTrack ( loopName ) ) ;
402- else {
403- var soundtrack = getSoundTrack ( loopName ) ;
404- soundtrack . updateVolume ( ) ;
405- soundtrack . loop ( ) ;
406- }
407- }
408- }
409- }
410-
411464
412465 // Centralized function for sound fading
413466 //
@@ -427,7 +480,6 @@ GNU General Public License for more details.
427480 // Note soundInterval and minVolume are declared globally (at top of the script)
428481 var maxVolume = 1.0 ; // This is native to JavaScript. Changing will cause unexpected behavior
429482 globalVolume = Math . max ( minVolume , Math . min ( maxVolume , globalVolume + ( soundInterval * direction ) ) ) ;
430-
431483 for ( var soundIndex in clips ) {
432484 if ( clips . hasOwnProperty ( soundIndex ) ) {
433485 clips [ soundIndex ] . updateVolume ( ) ;
@@ -457,8 +509,8 @@ GNU General Public License for more details.
457509 break ;
458510 case "number" :
459511 var tempNum = parseFloat ( func . args [ i ] ) ;
460- if ( tempNum <= 1.0 && volumeProportion === undefined ) volumeProportion = tempNum ;
461- else if ( overlap === undefined ) overlap = tempNum ;
512+ if ( volumeProportion === undefined && tempNum <= 1.0 ) volumeProportion = tempNum ;
513+ else if ( overlap === undefined && tempNum >= updateInterval ) overlap = tempNum ;
462514 break ;
463515 case "boolean" :
464516 if ( loop === undefined ) loop = func . args [ i ] ;
@@ -476,7 +528,7 @@ GNU General Public License for more details.
476528 if ( volumeProportion === undefined || volumeProportion > 1.0 || volumeProportion < 0.0 ) { return this . error ( "No volume proportion specified (must be a decimal number no smaller than 0.0 and no bigger than 1.0.)" ) ; }
477529 break ;
478530 case overlapLabel :
479- if ( overlap === undefined ) { return this . error ( "No fade duration specified (must be a number in milliseconds greater than 1 .)" ) ; }
531+ if ( overlap === undefined ) { return this . error ( "No fade duration specified (must be a number in milliseconds greater than + " + updateInterval + " ms .)") ; }
480532 break ;
481533 case loopLabel :
482534 if ( loop === undefined ) { return this . error ( "No loop flag provided (must be a boolean, aka true or false.)" ) ; }
@@ -635,7 +687,7 @@ GNU General Public License for more details.
635687 handler : function ( ) {
636688 for ( var clipName in clips ) {
637689 if ( clips . hasOwnProperty ( clipName ) ) {
638- if ( clips [ clipName ] !== undefined ) clips [ clipName ] . pause ( ) ;
690+ getSoundTrack ( clipName ) . pause ( ) ;
639691 }
640692 }
641693 }
@@ -760,11 +812,16 @@ GNU General Public License for more details.
760812 macros . add ( "fadeinsounds" , {
761813 handler : function ( ) {
762814
763- var args = manageCommonArgs ( this , [ clipNameLabel ] ) ;
815+ var clipNameString = this . args [ 0 ] ;
816+ if ( this . args [ 0 ] === undefined ) return ;
817+ clipNameString = this . args [ 0 ] . toString ( ) ;
818+ if ( clipNameString == "[]" ) return ;
819+ var clipNames = clipNameString . split ( "," ) ;
820+ if ( clipNames . length < 1 ) return ;
764821
765- var volumeProportion = args [ 1 ] !== undefined ? args [ 1 ] : undefined ;
766- var overlap = args [ 2 ] !== undefined ? args [ 2 ] : undefined ;
767- loopSounds ( this . args [ 0 ] . toString ( ) , true , volumeProportion , overlap ) ;
822+ for ( var index = 0 ; index < clipNames . length ; index ++ ) {
823+ fadeSound ( cleanClipName ( clipNames [ index ] ) , true ) ;
824+ }
768825 }
769826 } ) ;
770827
0 commit comments