Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
793 lines (793 sloc) 59.1 KB
(dp0
S'buildDestination'
p1
V/Users/Tory/Documents/workspace/sqTwineSound/sqTwineSound_demo.html
p2
sS'saveDestination'
p3
V/Users/tory/workspace/sqTwineSound/sqTwineSound_demo.tws
p4
sS'metadata'
p5
(dp6
sS'target'
p7
S'sugarcube_v103'
p8
sS'storyPanel'
p9
(dp10
S'widgets'
p11
(lp12
(dp13
S'selected'
p14
I00
sS'pos'
p15
(lp16
I14
aI12
asS'passage'
p17
(itiddlywiki
Tiddler
p18
(dp20
S'text'
p21
V<<set $sound = "Yes">>\u005c\u000a<<set $currentLoops = []>>\u005c\u000a<<set $background_music = "accordion.mp3">>\u005c\u000a<<set $footsteps = "footsteps.mp3">>\u005c\u000a<h2>Welcome to the sub-Q Sound Macro Suite Demo</h2>\u005c\u000a<h3>Macros</h3>\u005c\u000a<ul><li>[[playsound]], [[updatevolume|playsound]]</li>\u000a<li>[[pausesound]], [[pauseallsound|pausesound]]</li>\u000a<li>[[loopsound]], [[unloopsound|loopsound]], [[fadeinsound|loopsound]], [[fadeinsounds|loopsound]], [[fadeoutsound|loopsound]], [[fadeoutsounds|loopsound]], [[playsounds|loopsound]], [[stopsound|loopsound]], [[stopallsound|loopsound]]</li>\u000a<li>[[quieter]], [[louder|quieter]]</li>\u000a<li>[[jumpscare]]</li></ul>\u000a<h3>Overview</h3>\u005c\u000aThis suite builds on Leon Arnott's incredibly rad HTML5 sound macros by adding\u000a<ul><li>story-wide volume control</li>\u000a<li>individual volume control</li>\u000a<li>seamless loops (with crossfade)</li>\u000a<li>fade duration control</li>\u000a<li>the ability to start multiple audio tracks at once</li></ul>\u005c\u000a\u000aA macro is a piece of code you can insert in a Twine passage like so:\u000a{{{<<playsound $thisSound>>}}}\u000a\u000aThis demo shows what the sub-Q suite of sound macros can be used to do.\u000a\u000a<h3>Quick Reference</h3>\u005c\u000a<<display "questions">>\u005c\u000a\u000a<h3>Before We Start</h3>\u005c\u000aIn this demo, I use "clip" and "track" to mean the playback of a specific audio file.\u000a\u000aThese macros recognize the following file formats, but be advised not every format works in every browser: ogg, mp3, wav, and webm.\u000a\u000aFor optimal performance of these macros, the <a href="http://www.motoslave.net/sugarcube/" target="_blank">latest Sugarcube story format is recommended</a>. This demo uses Sugarcube v1.0.3.\u000a\u000aIn the Sugarcube story format, you can pass your audio clip name[s] as a string (e.g. "meow.mp3") or a variable (e.g. $meow, assuming you have defined {{{<<set $meow = "meow.mp3">>}}}.) But in Sugarcane and Jonah, the audio clip names must be passed as strings (e.g. "meow.mp3"). You will see both these strings and variables used in the examples.\u000a\u000aNote that, even in Sugarcube, string variables are recommended because they help you do things like this:\u000a\u000a&nbsp;&nbsp;&nbsp;&nbsp;{{{<<set $heartbeat = "heartbeat.mp3">>}}}\u000a&nbsp;&nbsp;&nbsp;&nbsp;{{{<<set $spookyMusic = "spookyMusic.mp3">>}}}\u000a&nbsp;&nbsp;&nbsp;&nbsp;{{{<<set $spookyClips = [$heartbeat, $spookyMusic]>>}}}\u000a&nbsp;&nbsp;&nbsp;&nbsp;...\u000a&nbsp;&nbsp;&nbsp;&nbsp;{{{<<fadeinsounds $spookyClips>>}}}\u000a&nbsp;&nbsp;&nbsp;&nbsp;{{{<<timedcontinue 2s>>}}}\u000a&nbsp;&nbsp;&nbsp;&nbsp;...But then things weren't so scary...\u000a&nbsp;&nbsp;&nbsp;&nbsp;{{{<<fadeoutsounds $spookyClips>>}}}\u000a\u000a(Speaking of, if you'd like the "timedcontinue" macro, it's available in the <a href="http://www.glorioustrainwrecks.com/node/5462" target="_blank">Glorious Trainwrecks Replace Macro Set</a>!)\u000a\u000aHuge thanks to Leon Arnott for founding these sound macros.\u000a\u000aFeel free to save this demo locally and import the HTML into Twine to inspect it. Remember that the JavaScript in this demo and on github is "beautiful," so it's easy to read. Before you paste it into a Twine project, you may want to <a href="http://jscompress.com/" target="_blank">minimize it</a> for efficiency.\u000a\u000aReady? Let's go!\u000a\u000a-> [[Walk through|playsound]] all macros\u000a
p22
sS'title'
p23
VStart
p24
sS'modified'
p25
ctime
struct_time
p26
((I2015
I1
I23
I15
I20
I44
I4
I23
I0
tp27
(dp28
tp29
Rp30
sS'tags'
p31
(lp32
sS'created'
p33
g26
((I2014
I9
I25
I13
I11
I20
I3
I268
I1
tp34
(dp35
tp36
Rp37
sbsa(dp38
g14
I00
sg15
(lp39
I10
aI150
asg17
(itiddlywiki
Tiddler
p40
(dp41
g21
Vsub-Q Demo\u000asqTwineSound \u000av. 0.8.0
p42
sg23
VStoryTitle
p43
sg25
g26
((I2014
I10
I2
I18
I7
I34
I3
I275
I1
tp44
(dp45
tp46
Rp47
sg31
(lp48
sg33
g26
((I2014
I9
I25
I13
I11
I20
I3
I268
I1
tp49
(dp50
tp51
Rp52
sbsa(dp53
g14
I00
sg15
(lp54
I10
aI290
asg17
(itiddlywiki
Tiddler
p55
(dp56
g21
V<<set $background_music = "accordion.mp3">>\u005c\u000a\u000asqTwineSound\u000asource & demo\u000aby Tory Hoke\u000a<a href="http://www.twitter.com/toryhoke" target="_blank">@toryhoke</a>\u000a<a href="http://www.toryhoke.com">toryhoke.com</a>\u000a\u000aBased on sound macros\u000aby Leon Arnott\u000aof <a href="http://www.glorioustrainwrecks.com" target="_blank">Glorious Trainwrecks</a>\u000a\u000a\u000aA little background music\u000a<p align="right"><<display "common controls">></p>\u000a\u000a[[Audio attributions|audio attribution]]\u000a\u000aDeveloped for\u000aTwine v 1.4.2\u000aSugarcube v 1.0+
p57
sg23
VStoryAuthor
p58
sg25
g26
((I2015
I1
I23
I15
I21
I12
I4
I23
I0
tp59
(dp60
tp61
Rp62
sg31
(lp63
sg33
g26
((I2014
I9
I25
I13
I11
I20
I3
I268
I1
tp64
(dp65
tp66
Rp67
sbsa(dp68
g14
I00
sg15
(lp69
I593
aI23
asg17
(itiddlywiki
Tiddler
p70
(dp71
g21
V<<set $currentLoops = []>>\u005c\u000a<<set $background_music = "accordion.mp3">>\u005c\u000a<<set $footsteps = "footsteps.mp3">>\u005c\u000a[[<- loopsound, fadesound etc.|loopsound]]<div align="right">[[jumpscare]]</div>\u005c\u000a\u000a<h2>quieter, louder</h2>\u005c\u000a\u000a<<display "common controls">>\u000a\u000aThese macros adjust the overall volume of the story. The relative proportions of individual audio clips will be preserved.\u000a\u000aThese macros do NOT change the system volume. If the reader has their system volume down to two bars, these controls adjust the volume within that two-bar range.\u000a\u000aThis means the reader can have rainymoods.com playing in one tab at a certain volume, and then your story playing in another tab at a different volume.\u000a\u000a<h3>{{{<<quieter>>}}}</h3>\u005c\u000aReduces the story's overall volume by 1/10th of the reader's current system volume.\u000a\u000a<h3>{{{<<louder>>}}}</h3>\u005c\u000aIncreases the story's overally volume by 1/10th of the reader's current system volume.\u000a\u000aTogether, these options create a 10-unit range for the reader to adjust your story's volume (within the reader's system volume.)\u000a\u000a[[<- loopsound, fadesound etc.|loopsound]]<div align="right">[[jumpscare]]</div>
p72
sg23
Vquieter
p73
sg25
g26
((I2015
I1
I23
I15
I21
I31
I4
I23
I0
tp74
(dp75
tp76
Rp77
sg31
(lp78
sg33
g26
((I2014
I9
I25
I13
I39
I9
I3
I268
I1
tp79
(dp80
tp81
Rp82
sbsa(dp83
g14
I00
sg15
(lp84
I166
aI17
asg17
(itiddlywiki
Tiddler
p85
(dp86
g21
V<<set $soft_meow = "soft_meow.mp3">>\u005c\u000a<<set $background_music = "accordion.mp3">>\u005c\u000a[[<- Back to start|Start]]<div align="right">[[pausesound, stopsound ->|pausesound]]</div>\u005c\u000a\u000a<h2>playsound, updatevolume</h2>\u005c\u000a\u000aPlay Cat Meow at full available volume: <<button "|>">><<stopsound $soft_meow>><<playsound $soft_meow 1.0>><</button>>\u000aPlay Cat Meow at 1/2 volume: <<button "|>">><<stopsound $soft_meow>><<playsound $soft_meow 0.5>><</button>>\u000aPlay Cat Meow at 1/4 volume, with fade in, on loop: <<button "|>">><<stopsound $soft_meow>><<fadeinsound $soft_meow 0.25>><</button>>\u000aStop All Sound: <<button "[]">><<stopallsound>><</button>>\u000a\u000a<h3>{{{<<playsound "introMusic.mp3" 0.5 200 true>>}}}</h3>\u005c\u000a{{{<<playsound>>}}} lets you do a little bit of sound mixing.\u000a\u000a Parameters:\u000a\u000a<ul><li>REQUIRED: clipName (e.g. "backgroundMusic.mp3" or $backgroundMusic)</li><li>OPTIONAL: decimal proportion of volume (0.0 being minimum/mute, and 1.0 being maximum/default)</li><li>OPTIONAL: number of milliseconds to overlap/crossfade the loop (1000 ms by default, must be >= 10 ms if declared)</li><li>OPTIONAL: true if you'd like to loop, false if no</li></ul>\u005c\u000a\u000aSo this plays a clip once, no fade, at full global volume:\u000a\u000a {{{<<playsound $walla">>}}}\u000a\u000aThis fades in a quiet background $walla that will loop and crossfade with 2000 ms (2 seconds) of overlap:\u000a \u000a {{{<<playsound $walla 0.2 2000 true>>}}}\u000a\u000a This plays $meow once, no fade, at loudest available volume:\u000a \u000a {{{<<playsound $meow 1.0>>}}}\u000a\u000aThe last used volume will be remembered when you stop and restart the clip.\u000a\u000aNOTE: It's best practice to stop a sound before attempting to play it again. Any attempt to play a sound already playing is ignored.\u005c\u000a\u000a<h3>{{{<<updatevolume $backgroundMusic 0.5>>}}}</h3>\u005c\u000aGiven a decimal between 0.0 and 1.0, update the clip's volume proportion and the clip's actual volume.\u000a\u000aParameters:\u000a\u000a<ul><li>REQUIRED: clipName (e.g. "backgroundMusic.mp3" or $backgroundMusic)</li><li>REQUIRED: decimal proportion of volume (0.0 being minimum/mute, and 1.0 being maximum/default)</li></ul>\u005c\u000a\u000a\u000aLoop Music at 0.50 volume: <<button "|>">><<fadeinsound $background_music 0.50>><</button>>\u000aChange volume to 0.10 volume: <<button "Volume 0.10">><<updatevolume $background_music 0.10>><</button>>\u000aChange volume to 0.90 volume: <<button "Volume 0.90">><<updatevolume $background_music 0.90>><</button>>\u000aStop Music: <<button "[]">><<stopsound $background_music>><</button>>\u000a\u000a[[<- Back to start|Start]]<div align="right">[[pausesound, stopsound ->|pausesound]]</div>\u005c
p87
sg23
Vplaysound
p88
sg25
g26
((I2015
I1
I23
I15
I21
I47
I4
I23
I0
tp89
(dp90
tp91
Rp92
sg31
(lp93
sg33
g26
((I2014
I9
I25
I13
I42
I9
I3
I268
I1
tp94
(dp95
tp96
Rp97
sbsa(dp98
g14
I00
sg15
(lp99
F307.0
aI20
asg17
(itiddlywiki
Tiddler
p100
(dp101
g21
V<<set $currentLoops = []>>\u005c\u000a<<set $background_music = "accordion.mp3">>\u005c\u000a<<set $footsteps = "footsteps.mp3">>\u005c\u000a[[<- playsound|playsound]]<div align="right">[[loopsound, fadesound, etc. ->|loopsound]]</div>\u005c\u000a\u000a<h2>pausesound, stopsound</h2>\u005c\u000a\u000a<<display "common controls">>\u000a\u000a<h3>{{{<<pausesound $background_music>>}}}</h3>\u005c\u000aPauses $background_music at its current location. \u000aUse {{{<<playsound $background_music>>}}} to resume it.\u000a\u000a\u000a<h3>{{{<<stopsound $backgroundMusic>>}}}</h3>\u005c\u000aStop the given sound immediately.\u000aIf the sound is played again, it will play from the beginning.\u000a\u000a\u000a\u000a[[<- playsound|playsound]]<div align="right">[[loopsound, fadesound, etc. ->|loopsound]]</div>\u005c
p102
sg23
Vpausesound
p103
sg25
g26
((I2015
I1
I23
I15
I22
I4
I4
I23
I0
tp104
(dp105
tp106
Rp107
sg31
(lp108
sg33
g26
((I2014
I9
I25
I13
I43
I34
I3
I268
I1
tp109
(dp110
tp111
Rp112
sbsa(dp113
g14
I00
sg15
(lp114
I445
aI20
asg17
(itiddlywiki
Tiddler
p115
(dp116
g21
V<<set $currentLoops = []>>\u005c\u000a<<set $background_music = "accordion.mp3">>\u005c\u000a<<set $footsteps = "footsteps.mp3">>\u005c\u000a[[<- pausesound, stopsound|pausesound]]<div align="right">[[quieter, louder ->|quieter]]</div>\u005c\u000a\u000a<h2>loopsound, unloopsound, fadeinsound, fadeinsounds, fadeoutsound, fadeoutsounds, playsounds, pauseallsound, stopallsound</h2>\u005c\u000a\u000a<<display "single loop controls">>\u000a<<display "multiple loop controls">>\u000a\u000aNote that you must keep track of what's playing--what you want to stop and start--on your own.\u000a\u000a\u000a<h3>{{{<<loopsound "accordion.mp3">>}}}</h3>\u005c\u000aThe parameters (in this order, please):\u000a\u000aParameters:\u000a\u000a<ul><li>REQUIRED: clipName (e.g. "backgroundMusic.mp3" or $backgroundMusic)</li><li>OPTIONAL: decimal proportion of volume (0.0 being minimum/mute, and 1.0 being maximum/default)</li><li>OPTIONAL: number of milliseconds to overlap/crossfade the loop (1000 ms by default, must be >= 10 ms if declared)</li></ul>\u005c\u000a\u000aStarts playing the given clip on repeat. Note that browsers will not necessarily play looping audio seamlessly.\u000a\u000a<h3>{{{<<unloopsound $footsteps>>}}}</h3>\u005c\u000aLet the given sound stop when it finishes its current loop (so the sound no longer repeats.)\u000a\u000a<h3>{{{<<fadeinsound "footsteps.mp3">>}}}</h3>\u005c\u000aIdentical to loopsound, but performs a fade in over the given overlap period.\u000a\u000a<h3>{{{<<fadeinsounds ["accordion.mp3", "footsteps.mp3"]>>}}}</h3>\u005c\u000aOR IDEALLY\u000a<h3>{{{<<set $spookySounds = [$moodMusic, $footSteps]>>}}}\u000a{{{<<fadeinsounds $spookySounds>>}}}</h3>\u005c\u000a\u000aFade in multiple sounds at once. Previous clip volume and overlap is remembered.\u000a\u000a<h3>{{{<<fadeoutsound $birdsong>>}}}</h3>\u005c\u000aIdentical to stopsound, but fades out the sound over 2 seconds.\u000a\u000a<h3>{{{<<fadeoutsounds ["moodMusic.mp3", "footsteps.mp3"]>>}}}</h3>\u005c\u000a&nbsp;&nbsp;&nbsp;&nbsp;OR IDEALLY\u000a<h3>{{{<<set $spookySounds = [$moodMusic, $footSteps]>>}}}\u000a{{{<<fadeoutsounds $spookySounds>>}}}</h3>\u005c\u000a\u000aFade out multiple sounds at once.\u000a\u000a<h3>{{{<<playsounds ["moodMusic.mp3", "footsteps.mp3"]>>}}}</h3>\u005c\u000a&nbsp;&nbsp;&nbsp;&nbsp;OR IDEALLY\u000a<h3>{{{<<set $spookySounds = [$moodMusic, $footSteps]>>}}}\u000a{{{<<playsounds $spookySounds>>}}}</h3>\u005c\u000a\u000aPlay multiple sounds at once (picking up where we left off)\u000a\u000aParameters:\u000a\u000a<ul><li>REQUIRED: clipName (e.g. "backgroundMusic.mp3" or $backgroundMusic)</li><li>OPTIONAL: decimal proportion of volume (0.0 being minimum/mute, and 1.0 being maximum/default)</li><li>OPTIONAL: number of milliseconds to overlap/crossfade the loop (1000 ms by default, must be >= 10 ms if declared)</li><li>OPTIONAL: true if you'd like to loop, false if no</li></ul>\u005c\u000a\u000a<h3>{{{<<pauseallsound>>}}}</h3>\u000aPauses all sounds at their current location. \u000a\u000a<h3>{{{<<stopallsound>>}}}</h3>\u000aStops all sounds immediately. If any stopped sound is played again, it will play from the beginning.\u000a\u000a[[<- pausesound, stopsound|pausesound]]<div align="right">[[quieter, louder ->|quieter]]</div>\u005c
p117
sg23
Vloopsound
p118
sg25
g26
((I2015
I1
I23
I15
I22
I19
I4
I23
I0
tp119
(dp120
tp121
Rp122
sg31
(lp123
sg33
g26
((I2014
I9
I25
I13
I44
I13
I3
I268
I1
tp124
(dp125
tp126
Rp127
sbsa(dp128
g14
I00
sg15
(lp129
I746
aI25
asg17
(itiddlywiki
Tiddler
p130
(dp131
g21
V<<set $currentLoops = []>>\u005c\u000a<<set $loud_meow = "cat_scream.mp3">>\u005c\u000a[[<- quieter & louder|quieter]]<div align="right">[[Back to intro|Start]]</div>\u005c\u000a\u000a<h2>jumpscare</h2>\u005c\u000a{{{<<jumpscare>>}}} plays a clip at the current system volume, regardless of whether the reader turned the story volume down.\u000a\u000aThis macro does NOT change the system volume. If the reader has their system volume down to two bars, the jumpscare sound will play at the full two bars' worth of volume.\u000a\u000aEven so, PLEASE WARN YOUR READERS BEFORE USING JUMPSCARE. Providing a warning about possible jump scares at the beginning of your story will\u000a\u000a<ul>\u005c\u000a<li>Increase suspense</li>\u000a<li>Increase/maintain reader goodwill toward Twine in general</li>\u000a<li>Prevent lawsuits</li>\u000a<li>Prevent me from feeling guilty for providing this macro</li>\u000a</ul>\u005c\u000a\u000aPlease use jumpscare responsibly.\u000a\u000aJump Scare: <<button "Reeeowwwr!">><<jumpscare $loud_meow>><</button>>\u000a\u000aThat's everything!\u000a\u000a<<display "questions">>\u000a\u000aEnjoy, and happy Twining.\u000a\u000a\u000a\u000a[[<- quieter & louder|quieter]]<div align="right">[[Back to intro|Start]]</div>\u000a
p132
sg23
Vjumpscare
p133
sg25
g26
((I2015
I1
I23
I15
I22
I29
I4
I23
I0
tp134
(dp135
tp136
Rp137
sg31
(lp138
sg33
g26
((I2014
I9
I25
I13
I45
I2
I3
I268
I1
tp139
(dp140
tp141
Rp142
sbsa(dp143
g14
I00
sg15
(lp144
F14.0
aF435.0
asg17
(itiddlywiki
Tiddler
p145
(dp146
g21
V/*\u000asqTwineSound HTML5 Sound Macro Suite\u000aCopyright 2014 Tory Hoke\u000a\u000aProgram URI: http://www.sub-q.com/plugins/sqTwineSound/\u000aDescription: Sound macros for Twine creations, including controls for volume, fade interval, and playing multiple tracks at once.\u000aVersion: 0.8.0\u000aAuthor: Tory Hoke\u000aAuthor URI: http://www.toryhoke.com\u000aLicense: GNU General Public License\u000aLicense URI: http://www.opensource.org/licenses/gpl-license.php\u000aRepository: https://github.com/AteYourLembas/sqTwineSound\u000aFAQ / Q & A: http://sub-q.com/questions (password: ThinkVast)\u000aBug Reports/Feature Requests: http://sub-q.com/forums/topic/what-would-you-like-to-see-sqtwinesound-do-that-its-not-doing/ (password: ThinkVast)\u000a\u000a sub-Q.com is password-protected while it's in beta (until January 2015.)\u000a Please kick the tires and report any issues with the website\u000a via the sub-Q.com Contact form.\u000a\u000a\u000aThis program based on:\u000aTwine: HTML5 sound macros by Leon Arnott of Glorious Trainwrecks\u000athe source and influence of which appear under a Creative Commons CC0 1.0 Universal License\u000a\u000aThis program uses\u000a\u000a easeInOutSine()\u000a Copyright © 2001 Robert Penner\u000a All rights reserved.\u000a \u000a As distributed by Kirupa\u000a http://www.kirupa.com/forum/showthread.php?378287-Robert-Penner-s-Easing-Equations-in-Pure-JS-(no-jQuery)\u000a \u000a Open source under the BSD License. \u000a \u000a \u000a Redistribution and use in source and binary forms, with or without modification, \u000a are permitted provided that the following conditions are met:\u000a \u000a Redistributions of source code must retain the above copyright notice, this list of \u000a conditions and the following disclaimer.\u000a Redistributions in binary form must reproduce the above copyright notice, this list \u000a of conditions and the following disclaimer in the documentation and/or other materials \u000a provided with the distribution.\u000a \u000a Neither the name of the author nor the names of contributors may be used to endorse \u000a or promote products derived from this software without specific prior written permission.\u000a \u000a THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY \u000a EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\u000a MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\u000a COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\u000a EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE\u000a GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED \u000a AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\u000a NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED \u000a OF THE POSSIBILITY OF SUCH DAMAGE. \u000a\u000aThis program is free software: you can redistribute it and/or modify\u000ait under the terms of the GNU General Public License as published by\u000athe Free Software Foundation, either version 3 of the License, or\u000a(at your option) any later version.\u000a \u000aThis program is distributed in the hope that it will be useful,\u000abut WITHOUT ANY WARRANTY; without even the implied warranty of\u000aMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\u000aGNU General Public License for more details.\u000a\u000a*/\u000a\u000a(function () {\u000a version.extensions.soundMacros = {\u000a major: 0,\u000a minor: 8,\u000a revision: 0\u000a };\u000a\u000a var globalVolume = 1.0;\u000a var updateInterval = 10; //Update sound volume, etc. once every 10 ms\u000a var defaultOverlap = 1000; //Default track overlap is 1000 ms\u000a var minVolume = 0.01; // Minimum possible volume -- 0 is mute, so we want somethings slightly above that\u000a var soundInterval = 0.1; // Creates an interval of 1/10 creates ten stages of loudness. Used by quieter/louder. Feel free to tweak\u000a var fileExtensions = ["ogg", "mp3", "wav", "webm"]; // Acceptable file extensions for audio\u000a var clips = {};\u000a\u000a // Convenience vars\u000a var clipNameLabel = "Clip Name";\u000a var overlapLabel = "Overlap";\u000a var volumeProportionLabel = "Volume Proportion";\u000a var loopLabel = "Loop?";\u000a\u000a\u000a //------------ Robert Penner via Kirupa math methods ----------\u000a //-------------------------------------------------------------\u000a\u000a function easeInOutSine(currentIteration, startValue, changeInValue, totalIterations) {\u000a return changeInValue / 2 * (1 - Math.cos(Math.PI * currentIteration / totalIterations)) + startValue;\u000a }\u000a\u000a\u000a //------------ End Math methods -------------------------------\u000a //-------------------------------------------------------------\u000a\u000a //------------- pausableTimeout ---------\u000a //--------------------------------------\u000a function pausableTimeout(func, params) {\u000a\u000a this.funcToRun = func;\u000a this.waitStartTime = -1;\u000a this.waitEndTime = -1;\u000a this.waitDuration = -1;\u000a\u000a this.activate = function(waitDuration) {\u000a\u000a if (this.pausedAt !== undefined) { this.waitDuration = this.timeRemaining(); }\u000a else if (waitDuration !== undefined) this.waitDuration = waitDuration;\u000a else if (this.waitDuration > -1 ) { console.log("Warning: No wait duration given to pausableTimeout. Using last specified one."); }\u000a else return; // Don't bother to start a loop with no wait duration\u000a\u000a this.waitStartTime = new Date().getTime();\u000a this.waitEndTime = new Date().getTime() + this.waitDuration;\u000a this.timeout = setTimeout(this.funcToRun, this.waitDuration, params);\u000a };\u000a\u000a this.deactivate = function() {\u000a this.pausedAt = this.timeElapsed();\u000a if (this.timeout !== undefined) clearTimeout(this.timeout);\u000a };\u000a\u000a this.stopAndClear = function() {\u000a if (this.pausedAt !== undefined) delete this.pausedAt;\u000a if (this.timeout !== undefined) { clearTimeout(this.timeout); delete this.timeout; }\u000a };\u000a\u000a this.timeElapsed = function() {\u000a return new Date().getTime() - this.waitStartTime;\u000a };\u000a\u000a this.timeRemaining = function() {\u000a if (this.pausedAt !== undefined) return this.waitDuration - this.pausedAt;\u000a return this.waitEndTime - new Date().getTime();\u000a };\u000a }\u000a //------------- /pausableTimeout --------\u000a //--------------------------------------\u000a\u000a\u000a //------------- sqAudio ----------------\u000a //--------------------------------------\u000a function sqAudio(fullPath, clipName, fileExt) {\u000a\u000a this.fullPath = fullPath;\u000a this.clipName = clipName; // Let a clip know its own name\u000a this.fileExt = fileExt;\u000a\u000a // Defaults\u000a this.volumeProportion = 1.0; // By default, full volume\u000a this.overlap = defaultOverlap; // By default, defaultOverlap ms\u000a this.isPlayable = false; // Assume audio is not playable\u000a this.looping = false; // Assume audio not looping\u000a this.alternate = false;\u000a this.mainAudio = new Audio();\u000a this.partnerAudio = new Audio();\u000a\u000a this.mainAudio.setAttribute("src", this.fullPath);\u000a if (this.mainAudio.canPlayType) {\u000a for (var i = -1; i < fileExtensions.length; i += 1) {\u000a if (i >= 0) fileExt = fileExtensions[i];\u000a if (this.mainAudio.canPlayType("audio/" + fileExt)) break;\u000a }\u000a if (i < fileExtensions.length) {\u000a this.mainAudio.interval = null;\u000a this.partnerAudio.setAttribute("src", this.fullPath);\u000a this.partnerAudio.interval = null;\u000a this.isPlayable = true;\u000a\u000a } else {\u000a console.log("Browser can't play '" + this.clipName + "'");\u000a }\u000a } \u000a\u000a // Convenience method for getting duration\u000a // TODO : protect this against audio not being loaded yet\u000a //\u000a this.getDuration = function () {\u000a\u000a return this.mainAudio.duration;\u000a };\u000a\u000a // Get what we consider the current audio track\u000a //\u000a this._getActiveAudio = function() {\u000a return (this.alternate) ? this.partnerAudio : this.mainAudio;\u000a };\u000a\u000a // Get what we consider the idle audio track\u000a //\u000a this._getIdleAudio = function() {\u000a return (this.alternate) ? this.mainAudio : this.partnerAudio;\u000a };\u000a\u000a\u000a\u000a // Perform fade on specified audio\u000a // Use ease\u000a //\u000a this.__fadeSound = function(audioObj, fadeIn) {\u000a\u000a var startVolume = fadeIn ? 0 : globalVolume * this.volumeProportion;\u000a var deltaVolume = globalVolume * this.volumeProportion * (fadeIn ? 1 : -1);\u000a\u000a //alert("__fadeSound! fadeIn " + fadeIn + ", globalVolume " + globalVolume + ", volProp " + this.volumeProportion + " startVol " + startVolume + " deltaVolume " + deltaVolume);\u000a\u000a // Handy vars for easing\u000a var totalIterations = this.overlap/updateInterval;\u000a var currentIteration = 1;\u000a\u000a audioObj.interval = setInterval(function() {\u000a\u000a //Use easing to prevent sound popping in or out\u000a //\u000a var desiredVolume = easeInOutSine(currentIteration, startVolume, deltaVolume, totalIterations);\u000a \u000a //alert("Well desiredVol is " + desiredVolume + " cos currIter " + currentIteration + " startVol " + startVolume + " delta vol " + deltaVolume + " total iter " + totalIterations);\u000a //This should never happen, but if it does, skip the fade\u000a if (isNaN(desiredVolume)) {\u000a audioObj.volume = startVolume + deltaVolume;\u000a console.log("There was a problem with the fade. Possibly overlap " + this.overlap + " is shorter than updateInterval " + updateInterval + "? ");\u000a } else {\u000a audioObj.volume = desiredVolume;\u000a }\u000a currentIteration += 1;\u000a \u000a if (audioObj.volume === (startVolume + deltaVolume)) { \u000a //alert("Grats! You reached your destination of " + audioObj.volume); \u000a clearInterval(audioObj.interval); \u000a }\u000a\u000a //This effectively stops the loop and poises the volume to be played again\u000a //That way the clip isn't needlessly looping when no one can hear it.\u000a if (audioObj.volume === 0) {\u000a audioObj.pause();\u000a audioObj.currentTime = 0;\u000a }\u000a }, updateInterval);\u000a\u000a };\u000a\u000a\u000a // Manages starting one loop before the last play has ended\u000a // and cross-fading the ends\u000a //\u000a this._crossfadeLoop = function(params) {\u000a\u000a var sqAudioObj = params[0];\u000a var currAudioObj = params[1];\u000a\u000a // Let loop expire if no longer looping\u000a //\u000a if (!sqAudioObj.looping) { return; }\u000a\u000a var nextAudioObj = sqAudioObj.alternate ? sqAudioObj.mainAudio : sqAudioObj.partnerAudio;\u000a sqAudioObj.alternate = !sqAudioObj.alternate;\u000a\u000a // Don't even bother with crossfade if there's no overlap\u000a if (sqAudioObj.overlap !== undefined && sqAudioObj.overlap > 1) {\u000a\u000a // fade out current sound\u000a //\u000a sqAudioObj._fadeSound(currAudioObj, false);\u000a\u000a // And fade in our partner\u000a //\u000a //nextAudioObj.volume = 0; \u000a //if (nextAudioObj.currentTime > 0) nextAudioObj.currentTime = 0;\u000a //nextAudioObj.play();\u000a sqAudioObj._fadeSound(nextAudioObj, true);\u000a\u000a }\u000a else {\u000a sqAudioObj.updateVolume(); \u000a nextAudioObj.currentTime = 0;\u000a nextAudioObj.play();\u000a }\u000a\u000a // Kick off the next timer to crossfade\u000a // Might as well garbage collect the old crossfadeTimeout, too.\u000a //\u000a //if (sqAudioObj.crossfadeTimeout !== undefined) { sqAudioObj.crossfadeTimeout.stopAndClear(); delete sqAudioObj.crossfadeTimeout; }\u000a //if (isNaN(sqAudioObj.getDuration())) { throwError("Can't loop because duration is not known (audio not loaded, probably not found.)"); return; }\u000a //sqAudioObj.crossfadeTimeout = new pausableTimeout(sqAudioObj._crossfadeLoop, [sqAudioObj, nextAudioObj]); \u000a //sqAudioObj.crossfadeTimeout.activate(sqAudioObj.getDuration()*1000-sqAudioObj.overlap);\u000a\u000a };\u000a\u000a\u000a this._fadeSound = function(activeAudioObj, fadeIn) {\u000a\u000a // Set the goal volume as a proportion of the global volume\u000a // (e.g. if global volume is 0.4, and volume proportion is 0.25, overall the goal volume is 0.1)\u000a //\u000a var goalVolume = globalVolume * this.volumeProportion;\u000a if (activeAudioObj.interval) clearInterval(activeAudioObj.interval);\u000a if (fadeIn) {\u000a if (activeAudioObj.currentTime > 0) activeAudioObj.currentTime = 0;\u000a activeAudioObj.volume = 0; \u000a this.loop();\u000a\u000a } else {\u000a\u000a if (!activeAudioObj.currentTime) return;\u000a activeAudioObj.volume = goalVolume;\u000a activeAudioObj.play();\u000a }\u000a this.__fadeSound(activeAudioObj, fadeIn);\u000a\u000a };\u000a\u000a\u000a // Fade sound on whatever the active audio is\u000a //\u000a this.fadeSound = function(fadeIn) {\u000a if (fadeIn) {\u000a this.stopAndClear();\u000a this.looping = true;\u000a }\u000a else this.looping = false;\u000a this._fadeSound(this._getActiveAudio(), fadeIn);\u000a };\u000a\u000a // Update volume proportion and volume of both audio clips\u000a //\u000a this.setVolumeProportion = function(volumeProportion) {\u000a this.volumeProportion = volumeProportion;\u000a };\u000a\u000a // Update volume of active audio clips (assumes vol proportion and global vol already set)\u000a //\u000a this.updateVolume = function() {\u000a\u000a //alert("about to set vol to " + globalVolume + " x " + this.volumeProportion);\u000a this._getActiveAudio().volume = globalVolume * this.volumeProportion;\u000a };\u000a\u000a // Play the current audio object and reactivate any paused timer\u000a //\u000a this.play = function(loop) {\u000a\u000a //If it's a loop we want, just loop and don't make a big deal out of it\u000a if (loop) this.loop();\u000a\u000a else {\u000a\u000a var activeAudioObj = this._getActiveAudio();\u000a if (activeAudioObj) { \u000a activeAudioObj.play();\u000a }\u000a }\u000a };\u000a\u000a // Pause whatever audio is currently playing and pause the timer, too\u000a //\u000a this.pause = function() {\u000a if (this.crossfadeTimeout !== undefined) this.crossfadeTimeout.deactivate();\u000a this._getActiveAudio().pause();\u000a };\u000a\u000a // Stop whatever audio is currently playing and dump the timer\u000a //\u000a this.stopAndClear = function() {\u000a var activeAudioObj = this._getActiveAudio();\u000a activeAudioObj.pause();\u000a if (activeAudioObj.currentTime > 0) activeAudioObj.currentTime = 0;\u000a if (this.crossfadeTimeout !== undefined) { this.crossfadeTimeout.stopAndClear(); delete this.crossfadeTimeout; }\u000a };\u000a\u000a\u000a // Loop the track\u000a //\u000a this.loop = function() {\u000a\u000a this.looping = true;\u000a var activeAudioObj = this._getActiveAudio();\u000a\u000a // Create new timeout if one does not already exist; otherwise just reuse the existing one\u000a //\u000a this.crossfadeTimeout = (this.crossfadeTimeout === undefined) ? new pausableTimeout(this._crossfadeLoop, [this, activeAudioObj]) : this.crossfadeTimeout; \u000a if (isNaN(this.getDuration())) { return throwError("Can't loop because duration is not known (audio not loaded, probably not found.)"); }\u000a this.crossfadeTimeout.activate((this.getDuration()*1000)-this.overlap);\u000a activeAudioObj.play();\u000a };\u000a\u000a\u000a }\u000a //------------ /sqAudio ----------------\u000a //--------------------------------------\u000a\u000a\u000a\u000a /***********************************************************\u000a * MAIN METHOD\u000a /***********************************************************\u000a /\u000a / Here be monsters. Proceed with caution.\u000a /\u000a */\u000a\u000a // Verify that the audio can be played in browser\u000a //\u000a function parseAudio(c) {\u000a\u000a var d = c.exec(div.innerHTML); // returns list of form ["url/to/audio.fileType",/to/audio,fileType]\u000a\u000a while(d) {\u000a if (d) {\u000a if (!clips.hasOwnProperty(d[1])) {\u000a\u000a var parser = document.createElement('a');\u000a parser.href = d[1].toString();\u000a var pathnameSubstrings = parser.pathname.split("/");\u000a var clipName = pathnameSubstrings[pathnameSubstrings.length-1];\u000a var sqAudioObj = new sqAudio(parser.href + "." + d[2].toString(), clipName, d[2].toString());\u000a if (sqAudioObj.isPlayable) { clips[clipName] = sqAudioObj;}\u000a }\u000a }\u000a d = c.exec(div.innerHTML); // yes, we could just do a do/while, but some envs don't like that\u000a }\u000a }\u000a\u000a // Parse all used audio file names\u000a // Use whatever store area element is available in the story format\u000a //\u000a var storeElement = (document.getElementById("store-area") ? document.getElementById("store-area") : document.getElementById("storeArea"));\u000a var div = storeElement.firstChild;\u000a while (div) {\u000a var b = String.fromCharCode(92);\u000a var q = '"';\u000a var re = "['" + q + "]([^" + q + "']*?)" + b + ".(" + fileExtensions.join("|") + ")['" + q + "]";\u000a parseAudio(new RegExp(re, "gi"));\u000a div = div.nextSibling;\u000a }\u000a /***********************************************************\u000a * END MAIN METHOD\u000a /***********************************************************/\u000a\u000a\u000a\u000a /***********************************************************\u000a * SUPPORTING FUNCTIONS FOR THE MACROS\u000a /***********************************************************\u000a /\u000a / Here be monsters.\u000a /\u000a */\u000a\u000a // Given the clipName, get the active soundtrack\u000a //\u000a function getSoundTrack(clipName) {\u000a clipName = cleanClipName(clipName.toString());\u000a if (!clips.hasOwnProperty(clipName)) { return throwError("Given clipName " + clipName + " does not exist in this project. Please check your variable names."); }\u000a return clips[clipName];\u000a\u000a }\u000a\u000a\u000a // Centralized function for sound fading\u000a //\u000a function fadeSound(clipName, fadeIn) {\u000a\u000a var soundtrack = getSoundTrack(clipName);\u000a if (soundtrack === "undefined") { return throwError("audio clip " + clipName + " not found"); } \u000a soundtrack.fadeSound(fadeIn);\u000a \u000a }\u000a\u000a\u000a // Adjust the volume of ALL audio in the page\u000a //\u000a function adjustVolume(direction) {\u000a\u000a // Note soundInterval and minVolume are declared globally (at top of the script)\u000a var maxVolume = 1.0; // This is native to JavaScript. Changing will cause unexpected behavior\u000a globalVolume = Math.max(minVolume, Math.min(maxVolume, globalVolume + (soundInterval * direction)));\u000a for (var soundIndex in clips) {\u000a if (clips.hasOwnProperty(soundIndex)) {\u000a clips[soundIndex].updateVolume();\u000a }\u000a }\u000a }\u000a\u000a // Common argument management\u000a // Because of the total expected arguments (one string, one float, one int, one boolean)\u000a // This method attempts to be forgiving of sequence. \u000a // Be advised if there were even one more argument, it probably couldn't be so forgiving anymore!\u000a //\u000a function manageCommonArgs(func, requiredArgs) {\u000a\u000a // Look at the list of available arguments, clean them up, and take the first one of each desired type:\u000a // Recreate the arguments as a list in required sequence [clipName, volumeProportion, overlap, loop]\u000a\u000a var clipName;\u000a var volumeProportion;\u000a var overlap;\u000a var loop;\u000a\u000a for (var i = 0; i < func.args.length; i++) {\u000a switch (typeof func.args[i]) {\u000a case "string" :\u000a if (clipName === undefined) clipName = func.args[i].toString();\u000a break;\u000a case "number" :\u000a var tempNum = parseFloat(func.args[i]);\u000a if (volumeProportion === undefined && tempNum <= 1.0) volumeProportion = tempNum;\u000a else if (overlap === undefined && tempNum >=updateInterval) overlap = tempNum; \u000a break;\u000a case "boolean" :\u000a if (loop === undefined) loop = func.args[i];\u000a break;\u000a }\u000a }\u000a\u000a for (var requiredArg in requiredArgs) {\u000a if (requiredArgs.hasOwnProperty(requiredArg)) {\u000a switch (requiredArg) {\u000a case clipNameLabel :\u000a if (clipName === undefined) { return throwError("No audio clip name specified."); } \u000a break;\u000a case volumeProportionLabel :\u000a if (volumeProportion === undefined || volumeProportion > 1.0 || volumeProportion < 0.0) { return throwError("No volume proportion specified (must be a decimal number no smaller than 0.0 and no bigger than 1.0.)"); }\u000a break;\u000a case overlapLabel :\u000a if (overlap === undefined) { return throwError("No fade duration specified (must be a number in milliseconds greater than + " + updateInterval + " ms.)"); }\u000a break;\u000a case loopLabel :\u000a if (loop === undefined) { return throwError("No loop flag provided (must be a boolean, aka true or false.)"); }\u000a break;\u000a }\u000a }\u000a }\u000a return [clipName, volumeProportion, overlap, loop];\u000a }\u000a\u000a // Get the clipName up to the . if a . exists, otherwise do no harm\u000a //\u000a function cleanClipName(clipName) {\u000a\u000a var parser = document.createElement('a');\u000a parser.href = clipName.toString();\u000a var pathnameSubstrings = parser.pathname.split("/");\u000a clipName = pathnameSubstrings[pathnameSubstrings.length-1];\u000a return clipName.lastIndexOf(".") > -1 ? clipName.slice(0, clipName.lastIndexOf(".")) : clipName;\u000a }\u000a\u000a\u000a /***********************************************************\u000a * END SUPPORTING FUNCTIONS FOR THE MACROS\u000a /***********************************************************/\u000a\u000a\u000a\u000a /***********************************************************\u000a /***********************************************************\u000a * MACROS\u000a /***********************************************************\u000a /***********************************************************\u000a */\u000a\u000a /* updatevolume\u000a \u000a Given a decimal between 0.0 and 1.0, \u000a updates the clip's volume proportion and the clip's actual volume\u000a \u000a */\u000a macros.add("updatevolume", {\u000a handler: function () {\u000a \u000a var args = manageCommonArgs(this, [clipNameLabel, volumeProportionLabel]);\u000a var soundtrack = getSoundTrack(this.args[0]);\u000a soundtrack.setVolumeProportion(args[1]);\u000a soundtrack.updateVolume();\u000a }\u000a });\u000a\u000a /** playsound \u000a\u000a This version of the macro lets you do a little bit of sound mixing.\u000a \u000a Parameters:\u000a\u000a REQUIRED: clipName \u000a OPTIONAL: decimal proportion of volume (0.0 being minimum/mute, and 1.0 being maximum/default)\u000a OPTIONAL: number of milliseconds to overlap/crossfade the loop (0 ms by default)\u000a OPTIONAL: true if you'd like to loop, false if no\u000a \u000a \u000a */\u000a macros.add("playsound", {\u000a handler : function () {\u000a\u000a var args = manageCommonArgs(this, [clipNameLabel]);\u000a\u000a var soundtrack = getSoundTrack(this.args[0]);\u000a var volumeProportion = args[1] !== undefined ? args[1] : soundtrack.volumeProportion;\u000a soundtrack.overlap = args[2] !== undefined ? args[2] : defaultOverlap;\u000a var loop = args[3] !== undefined ? args[3] : false;\u000a soundtrack.setVolumeProportion(volumeProportion);\u000a soundtrack.updateVolume();\u000a soundtrack.play(loop); \u000a }\u000a });\u000a\u000a\u000a /* playsounds\u000a \u000a Play multiple sounds at once (picking up where we left off)\u000a If you give it no sounds to play, it quietly ignores the command.\u000a\u000a Parameters:\u000a\u000a OPTIONAL: clipName\u000a OPTIONAL: decimal proportion of volume (0.0 being minimum/mute, and 1.0 being maximum/default)\u000a OPTIONAL: number of milliseconds to overlap/crossfade (0 ms by default)\u000a OPTIONAL: true if you'd like to loop, false if no\u000a \u000a /\u000a */\u000a macros.add("playsounds", {\u000a handler: function () {\u000a\u000a var clipNameString = this.args[0];\u000a if (this.args[0] === undefined || this.args[0] == "") return;\u000a clipNameString = this.args[0].toString();\u000a if (clipNameString == "[]") return;\u000a var clipNames = clipNameString.split(",");\u000a if (clipNames.length < 1) return;\u000a var args = manageCommonArgs(this);\u000a for (var index = 0; index < clipNames.length; index++) {\u000a var soundtrack = getSoundTrack(cleanClipName(clipNames[index]));\u000a var volumeProportion = args[1] !== undefined ? args[1] : soundtrack.volumeProportion;\u000a soundtrack.overlap = args[2] !== undefined ? args[2] : defaultOverlap;\u000a var loop = args[3] !== undefined ? args[3] : false;\u000a soundtrack.setVolumeProportion(volumeProportion);\u000a soundtrack.updateVolume();\u000a soundtrack.play(loop); \u000a }\u000a }\u000a });\u000a\u000a\u000a\u000a /* pausesound\u000a \u000a Pauses clip at its current location. \u000a Use playsound to resume it.\u000a\u000a Parameters:\u000a\u000a REQUIRED: clipName\u000a\u000a */ \u000a macros.add("pausesound", {\u000a handler: function() {\u000a var args = manageCommonArgs(this, [clipNameLabel]); \u000a getSoundTrack(this.args[0]).pause();\u000a }\u000a });\u000a\u000a\u000a /* <<pauseallsound>> \u000a \u000a Pauses all sounds at their current location. \u000a \u000a If you'd like the option to start multiple sounds,\u000a take a look at <<playsounds>> and <<fadeinsounds>>\u000a */ \u000a macros.add("pauseallsound", {\u000a handler: function () {\u000a for (var clipName in clips) {\u000a if (clips.hasOwnProperty(clipName)) {\u000a getSoundTrack(clipName).pause();\u000a }\u000a }\u000a }\u000a });\u000a\u000a /* stopsound\u000a \u000a Stop the given sound immediately\u000a If the sound is played again, it will play from the beginning\u000a \u000a Parameters:\u000a\u000a REQUIRED: clipName \u000a */ \u000a macros.add("stopsound", {\u000a handler: function() {\u000a var args = manageCommonArgs(this, [clipNameLabel]); \u000a getSoundTrack(this.args[0]).stopAndClear();\u000a }\u000a });\u000a\u000a\u000a /* <<stopallsound>>\u000a \u000a Stops all sounds immediately.\u000a If any stopped sound is played again, it will play from the beginning\u000a \u000a If you'd like the option to start multiple sounds,\u000a take a look at <<playsounds>> and <<fadeinsounds>>\u000a */ \u000a macros.add("stopallsound", {\u000a handler: function () {\u000a for (var clipName in clips) {\u000a if (clips.hasOwnProperty(clipName)) {\u000a if (clips[clipName] !== undefined) clips[clipName].stopAndClear();\u000a }\u000a }\u000a }\u000a });\u000a\u000a /* loopsound\u000a \u000a Starts playing the given clip on repeat.\u000a Note that browsers will not necessarily play looping audio seamlessly.\u000a For seamless audio, use a fade duration/overlap (third parameter) greater than 1 millisecond\u000a (Well, you probably want something more perceptibe than 1 millisecond!)\u000a \u000a Parameters:\u000a\u000a REQUIRED: clipName \u000a OPTIONAL: decimal proportion of volume (0.0 being minimum/mute, and 1.0 being maximum/default)\u000a OPTIONAL: number of milliseconds to overlap/crossfade the loop (0 ms by default)\u000a */ \u000a macros.add("loopsound", {\u000a handler: function () {\u000a \u000a var args = manageCommonArgs(this, [clipNameLabel]);\u000a var soundtrack = getSoundTrack(this.args[0]);\u000a var volumeProportion = args[1] !== undefined ? args[1] : soundtrack.volumeProportion;\u000a soundtrack.overlap = args[2] !== undefined ? args[2] : defaultOverlap;\u000a soundtrack.setVolumeProportion(volumeProportion);\u000a soundtrack.updateVolume();\u000a soundtrack.loop();\u000a }\u000a });\u000a\u000a\u000a /* unloopsound \u000a \u000a Let the given sound stop when it finishes its current loop\u000a (so the sound no longer repeats.)\u000a\u000a Parameters:\u000a\u000a REQUIRED: clipName \u000a\u000a */ \u000a macros.add("unloopsound", {\u000a handler: function () {\u000a var args = manageCommonArgs(this, [clipNameLabel]); \u000a getSoundTrack(this.args[0]).looping = false;\u000a }\u000a });\u000a\u000a\u000a /* fadeinsound\u000a \u000a Identical to loopsound, but fades in the sound over 2 seconds.\u000a\u000a Parameters:\u000a\u000a REQUIRED: clipName\u000a OPTIONAL: decimal proportion of volume (0.0 being minimum/mute, and 1.0 being maximum/default)\u000a OPTIONAL: number of milliseconds to overlap/crossfade the loop (defaults to clip's last set overlap)\u000a\u000a */\u000a macros.add("fadeinsound", {\u000a handler: function () {\u000a\u000a var args = manageCommonArgs(this, [clipNameLabel]);\u000a \u000a var soundtrack = getSoundTrack(this.args[0]);\u000a var volumeProportion = args[1] !== undefined ? args[1] : soundtrack.volumeProportion; \u000a soundtrack.overlap = args[2] !== undefined ? args[2] : soundtrack.overlap;\u000a soundtrack.volumeProportion=volumeProportion;\u000a soundtrack.fadeSound(true);\u000a }\u000a });\u000a\u000a /* fadeinsounds\u000a\u000a Fade in multiple sounds at once.\u000a \u000a Parameters:\u000a\u000a REQUIRED: clipNames as list \u000a OPTIONAL: decimal proportion of volume (0.0 being minimum/mute, and 1.0 being maximum/default)\u000a OPTIONAL: number of milliseconds to overlap/crossfade the loop (defaults to clip's last set overlap)\u000a \u000a */\u000a macros.add("fadeinsounds", {\u000a handler: function () {\u000a\u000a var clipNameString = this.args[0];\u000a if (this.args[0] === undefined || this.args[0] == "") return;\u000a clipNameString = this.args[0].toString();\u000a if (clipNameString == "[]") return;\u000a var clipNames = clipNameString.split(",");\u000a if (clipNames.length < 1) return;\u000a\u000a var args = manageCommonArgs(this);\u000a\u000a for (var index = 0; index < clipNames.length; index++) {\u000a var soundtrack = getSoundTrack(this.args[0]);\u000a var volumeProportion = args[1] !== undefined ? args[1] : soundtrack.volumeProportion; \u000a soundtrack.overlap = args[2] !== undefined ? args[2] : soundtrack.overlap;\u000a soundtrack.volumeProportion=volumeProportion;\u000a soundtrack.fadeSound(true); \u000a }\u000a }\u000a });\u000a\u000a /* fadeoutsound\u000a \u000a Identical to stopsound, but fades out the sound over the stored fade duration (overlap).\u000a \u000a Parameters:\u000a\u000a REQUIRED: clipName \u000a\u000a */\u000a macros.add("fadeoutsound", {\u000a handler: function () {\u000a var args = manageCommonArgs(this, [clipNameLabel]); \u000a fadeSound(this.args[0].toString(), false);\u000a }\u000a });\u000a\u000a\u000a /* fadeoutsounds\u000a \u000a Fade out multiple sounds at once.\u000a If you give it no sounds to play, it quietly ignores the command.\u000a\u000a Parameters:\u000a\u000a REQUIRED: clipNames as list \u000a \u000a */\u000a macros.add("fadeoutsounds", {\u000a handler: function () {\u000a\u000a var clipNameString = this.args[0];\u000a if (this.args[0] === undefined) return;\u000a clipNameString = this.args[0].toString();\u000a if (clipNameString == "[]") return;\u000a var clipNames = clipNameString.split(",");\u000a if (clipNames.length < 1) return;\u000a\u000a for (var index = 0; index < clipNames.length; index++) {\u000a fadeSound(cleanClipName(clipNames[index]), false);\u000a }\u000a }\u000a });\u000a\u000a\u000a /* <<quieter>>\u000a \u000a Reduces the story's globalVolume by 1/10th of the reader's system volume.\u000a Thus creates a 10-unit volume range for the story\u000a \u000a */\u000a macros.add("quieter", {\u000a handler: function () {\u000a adjustVolume(-1);\u000a }\u000a });\u000a\u000a /* <<louder>>\u000a \u000a Increases the story's globalVolume by 1/10th of the reader's system volume.\u000a Thus creates a 10-unit volume range for the story\u000a \u000a */\u000a macros.add("louder", {\u000a handler: function () {\u000a adjustVolume(1);\u000a }\u000a });\u000a\u000a\u000a /* jumpscare\u000a \u000a Play the clip at maximum story volume\u000a Don't affect any stored volume options\u000a PLEASE GIVE THE READER A STARTLE WARNING BEFORE USING THIS.\u000a \u000a */\u000a macros.add("jumpscare", {\u000a handler: function () {\u000a var args = manageCommonArgs(this, [clipNameLabel]);\u000a var soundtrack = getSoundTrack(this.args[0]);\u000a soundtrack.setVolumeProportion(1.0);\u000a soundtrack.updateVolume();\u000a soundtrack.play();\u000a }\u000a });\u000a\u000a /***********************************************************\u000a * END MACROS\u000a /***********************************************************/\u000a\u000a\u000a\u000a}());\u000a\u000a// You read the whole thing! THAT'S PRETTY RAD. Keep up the good work, and happy Twining.\u000a\u000a
p147
sg23
VsqTwineSound Macros v0.8.0
p148
sg25
g26
((I2014
I10
I7
I0
I27
I14
I1
I280
I1
tp149
(dp150
tp151
Rp152
sg31
(lp153
Vscript
p154
asg33
g26
((I2014
I9
I25
I14
I26
I30
I3
I268
I1
tp155
(dp156
tp157
Rp158
sbsa(dp159
g14
I00
sg15
(lp160
I778
aI534
asg17
(itiddlywiki
Tiddler
p161
(dp162
g21
V<table>\u005c\u000a<tr><td>Fade in Music at 1/4 Volume w/ Tiny Fade/Crossfade: </td><td><<button "Fade In">><<set $currentLoops.push($background_music)>><<fadeinsound $background_music 0.25 80>><</button>></td></tr>\u005c\u000a<tr><td>Unloop Music: </td><td><<button "Unloop">><<set $currentLoops.splice($currentLoops.indexOf($background_music),1)>>\u005c\u000a<<unloopsound $background_music>><</button>></td></tr>\u005c\u000a<tr><td>Fade Out Music: </td><td><<button "Fade Out">><<set $currentLoops.splice($currentLoops.indexOf($background_music),1)>>\u005c\u000a<<fadeoutsound $background_music>><</button>></td></tr>\u005c\u000a<tr><td>Pause Music: </td><td><<button "||">><<pausesound $background_music>><</button>></td></tr>\u000a<tr><td>Resume Music Loop (pick up where we left off): </td><td><<button "|>">><<set $currentLoops.push($background_music)>><<playsound $background_music true>><</button>></td></tr>\u005c\u000a<tr><td>Stop Music: </td><td><<button "[]">><<set $currentLoops.splice($currentLoops.indexOf($background_music),1)>>\u005c\u000a<<stopsound $background_music>><</button>></td></tr>\u005c\u000a</table>\u005c
p163
sg23
Vsingle loop controls
p164
sg25
g26
((I2014
I10
I3
I9
I42
I18
I4
I276
I1
tp165
(dp166
tp167
Rp168
sg31
(lp169
sg33
g26
((I2014
I9
I25
I15
I8
I13
I3
I268
I1
tp170
(dp171
tp172
Rp173
sbsa(dp174
g14
I00
sg15
(lp175
F774.0
aF681.0
asg17
(itiddlywiki
Tiddler
p176
(dp177
g21
V<table>\u005c\u000a<tr><td>Fade in Footsteps at 3/4 volume w/ 2 second fade: </td><td><<button "Fade in">><<set $currentLoops.push($footsteps)>><<fadeinsound $footsteps 0.75 2000>><</button>></td></tr>\u005c\u000a<tr><td>Fade out Footsteps: </td><td><<button "Fade out">><<set $currentLoops.splice($currentLoops.indexOf($footsteps),1)>>\u005c\u000a<<fadeoutsound $footsteps>><</button>></td></tr>\u005c\u000a<tr><td>Fade Out All Current Loops: </td><td><<button "Fade Out All">><<fadeoutsounds $currentLoops>><<set $currentLoops.splice($currentLoops.indexOf($background_music),1)>><<set $currentLoops.splice($currentLoops.indexOf($footsteps),1)>><</button>></td></tr>\u005c\u000a<tr><td>Pause All Sounds : </td><td><<button "*||*">><<pauseallsound>><</button>></td></tr>\u005c\u000a<tr><td>Resume Any Paused Sounds/Loops : </td><td><<button "Resume All">><<playsounds $currentLoops>><</button>></td></tr>\u005c\u000a<tr><td>Stop All Sound : </td><td><<button "*[]*">><<stopallsound>><<set $currentLoops.splice($currentLoops.indexOf($background_music),1)>><<set $currentLoops.splice($currentLoops.indexOf($footsteps),1)>><</button>></td></tr>\u005c\u000a</table>
p178
sg23
Vmultiple loop controls
p179
sg25
g26
((I2014
I10
I4
I12
I32
I34
I5
I277
I1
tp180
(dp181
tp182
Rp183
sg31
(lp184
sg33
g26
((I2014
I9
I25
I15
I37
I20
I3
I268
I1
tp185
(dp186
tp187
Rp188
sbsa(dp189
g14
I00
sg15
(lp190
F162.0
aF282.0
asg17
(itiddlywiki
Tiddler
p191
(dp192
g21
VAll sample sounds acquired from <a href="http://soundbible.com/" target="_blank">soundbible.com</a>.\u000a\u000a<ul>\u000a<li><a href="http://soundbible.com/1954-Cat-Meow-2.html" target="_blank">Soft meow</a></li>\u000a<li><a href="http://soundbible.com/1509-Cat-Scream.html" target="_blank">Cat scream</a></li>\u000a<li><a href="http://soundbible.com/528-Accordion.html" target="_blank">Accordion Music</a></li>\u000a<li><a href="http://soundbible.com/2057-Footsteps-On-Cement.html" target="_blank">Footsteps</a></li>\u000a</ul>\u000a\u000a[[Back to start|Start]]
p193
sg23
Vaudio attribution
p194
sg25
g26
((I2014
I9
I25
I16
I5
I18
I3
I268
I1
tp195
(dp196
tp197
Rp198
sg31
(lp199
sg33
g26
((I2014
I9
I25
I16
I0
I22
I3
I268
I1
tp200
(dp201
tp202
Rp203
sbsa(dp204
g14
I00
sg15
(lp205
I344
aI217
asg17
(itiddlywiki
Tiddler
p206
(dp207
g21
V<h4>More info about these macros</h4>\u005c\u000a<ul><li><a href="https://github.com/AteYourLembas/sqTwineSound" target="_blank">Get the source and more on github</a></li><li><a href="http://sub-q.com/questions" target="_blank">FAQ/Q&A</a> (on the sub-Q site)</li>\u000a<li><a href="http://sub-q.com/forums/topic/what-would-you-like-to-see-sqtwinesound-do-that-its-not-doing/" target="_blank">Feature Requests</a> (on the sub-Q site)</li></ul>
p208
sg23
Vquestions
p209
sg25
g26
((I2014
I10
I6
I20
I59
I5
I0
I279
I1
tp210
(dp211
tp212
Rp213
sg31
(lp214
sg33
g26
((I2014
I9
I25
I17
I12
I8
I3
I268
I1
tp215
(dp216
tp217
Rp218
sbsa(dp219
g14
I00
sg15
(lp220
I777
aI377
asg17
(itiddlywiki
Tiddler
p221
(dp222
g21
V<<button "[]">><<stopallsound>><</button>>\u005c\u000a<<button "||">><<pauseallsound>><</button>>\u005c\u000a<<button "<)">><<quieter>><</button>>\u005c\u000a<<button "<)))">><<louder>><</button>>\u005c\u000a<<button "|>">><<playsound $background_music 0.5>><</button>>\u005c\u000a<<button "Loop simple">><<loopsound $background_music 0.5>><</button>>\u005c\u000a<<button "Loop w/crossfade">><<fadeinsound $background_music 0.5 80>><</button>>
p223
sg23
Vcommon controls
p224
sg25
g26
((I2014
I10
I3
I11
I15
I44
I4
I276
I1
tp225
(dp226
tp227
Rp228
sg31
(lp229
sg33
g26
((I2014
I9
I25
I18
I6
I18
I3
I268
I1
tp230
(dp231
tp232
Rp233
sbsa(dp234
g14
I00
sg15
(lp235
I17
aI720
asg17
(itiddlywiki
Tiddler
p236
(dp237
g21
V/* Your story will use the CSS in this passage to style the page.\u000aGive this passage more tags, and it will only affect passages with those tags.\u000aExample selectors: */\u000a\u000abody {\u000a /* This affects the entire page */\u000a color: #FFF;\u000a background-color: #587e7e;\u000a \u000a}\u000a\u000a#ui-bar {\u000a width: 250px;\u000a\u000a}\u000a\u000a\u000a#passages {\u000a margin-left: 125px;\u000a width: 75%;\u000a min-height: 100%;\u000a padding-bottom: 0;\u000a margin-bottom: 0;\u000a border: 0;\u000a}\u000a\u000a.passage {\u000a /* This only affects passages */\u000a font-size: medium;\u000a \u000a}\u000a.passage a {\u000a /* This affects passage links */\u000a color: #F2F5A9;\u000a \u000a}\u000a.passage a:hover {\u000a /* This affects links while the cursor is over them */\u000a \u000a color: #FFBF00;\u000a\u000a}
p238
sg23
Vmain
p239
sg25
g26
((I2014
I9
I30
I18
I0
I50
I1
I273
I1
tp240
(dp241
tp242
Rp243
sg31
(lp244
Vstylesheet
p245
asg33
g26
((I2014
I9
I25
I20
I41
I53
I3
I268
I1
tp246
(dp247
tp248
Rp249
sbsasS'scale'
p250
I1
sS'snapping'
p251
I00
ss.