-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSoundControl.as
More file actions
305 lines (261 loc) · 8.6 KB
/
Copy pathSoundControl.as
File metadata and controls
305 lines (261 loc) · 8.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
package iphstich.library
{
import flash.display.MovieClip;
import flash.events.Event;
import flash.media.Sound;
import flash.media.SoundChannel;
import flash.media.SoundTransform;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.utils.Dictionary;
import flash.utils.getTimer;
/**
* ...
* @author IphStich
*/
public class SoundControl
{
public static const CATEGORY_GLOBAL:Object = new Object();
public static const CATEGORY_BGM:String = "BGM";
public static const CATEGORY_SFX:String = "SFX";
private static var globalVolume:Number = 1;
private static var sounds:Dictionary = new Dictionary();
private static var volumes:Dictionary = new Dictionary();
private static var introChannelToClip = new Dictionary();
private static var dummyClip:MovieClip = new MovieClip();
private static var playing:Vector.<Playing>;
// Have to use this, because you can't initialise 'playing' with the static class
private static function checkInit () : void
{
if (playing == null)
{
volumes[CATEGORY_BGM] = 1;
playing = new Vector.<Playing>();
}
}
/**
* Use this function to add a pre-existing sound to the system, allowing you to easily play it later.
* @param source The Sound object that should be added
* @param label This is the label you will use later to play the sound, using <b>playSound()</b>
* @param maxCount The maximum number of instances of this sound that can play at once. If this value is exceeded at runtime, the olded sounds will be killed
* @param category The category this sound belongs to. Useful in adjusting volume levels in broad strokes using <b>adjustVolume()</b>
* @param volumeMultiplier This number is multiplied by the category volume to calculate the actual volume when this sound is played
*/
public static function addSound (source:Sound, label:*, maxCount:int=0, category:* = CATEGORY_SFX, volumeMultiplier:Number = 1) : void
{
checkInit();
if (sounds[label] != undefined) trace("WARNING: A sound clip already exists with name \"" + label + "\"");
var clip:SoundClip = new SoundClip();
clip.source = source;
clip.label = label;
clip.count = maxCount;
clip.category = category;
clip.volume = volumeMultiplier;
sounds[label] = clip;
if (volumes[category] == undefined) volumes[category] = 1;
}
/**
* For loading a sound from an external source
* @param source
*/
//public function loadSound (source:String) : void
//{
//var request:URLRequest = new URLRequest(source);
//var loader:URLLoader = new URLLoader();
//}
/**
* Use this function to play a sound effect after its been added.
* @param label The label used when adding the sound.
*/
public static function playSound (label:*) : void
{
play( sounds[label] );
}
/**
* Use this function to add a pre-existing piece of <b>back ground music</b> to the system.
* Background music operates under slightly different parameters compared with standard sounds
* @param source The Sound object that should be added as BGM
* @param label This is the label you will use later to play the music, using <b>playBGM()</b>
* @param loopTime This is the time in the music that the system will loop back to
* @param volumeMultiplier This number is multiplied by the category volume to calculate the actual volume when this music is played
*/
public static function addBGM (source:Sound, label:*, loopTime:Number, volumeMultiplier:Number = 1) : void
{
checkInit();
if (sounds[label] != undefined) trace("WARNING: A music clip already exists with name \"" + label + "\"");
var clip:SoundClip = new SoundClip();
clip.source = source;
clip.label = label;
clip.loopTime = loopTime;
clip.category = CATEGORY_BGM;
clip.volume = volumeMultiplier;
sounds[label] = clip;
}
/**
* This function will play a piece of background music.
* @param label The label you assigned it when you added it
* @param playIntro Whether or not to play the intro for the music, and then loop it, or just start off by looping it
* @param stopOld If true, will stop whichever piece of BGM is currently playing before starting this piece
*/
public static function playBGM (label:*, playIntro:Boolean = true, stopOld:Boolean = true) : void
{
if (stopOld) stop();
var clip:SoundClip = (sounds[label] as SoundClip);
if (playIntro)
{
if (!dummyClip.hasEventListener(Event.ENTER_FRAME)) dummyClip.addEventListener(Event.ENTER_FRAME, introChecks);
play (clip);
introChannelToClip [ playing[playing.length-1].channel ] = clip;
}
else
{
loopBGM (clip);
}
}
/**
* Use this to immediately end a piece of BGM or a SFX.
* @param label If <b>null</b> will stop whichever piece of BGM is currently playing. Use a label to check and cancel a specific piece of BGM.
*/
public static function stop (label:* = null) : void
{
var i:int;
var p:Playing;
for (i=playing.length-1; i>=0; --i)
//p in playing)
{
p = playing[i];
if ( p.clip.label == label || (label == null && p.clip.category == CATEGORY_BGM) )
{
p.channel.stop();
playing.splice(i, 1);
}
}
}
/**
* Use this to set the volume level of all sounds of a category.
* Useful with option menus that adjust the volume of sounds based on type / category
* @param category The category the sound was entered under. Use <b>SoundControl.CATEGORY_BGM</b> for background music.
* @param value the volume level of the sound category. The standard practice is to have this value between 0 and 1. But values above 1 are acceptable.
*/
public static function adjustVolume (category:*, value:Number) : void
{
checkInit();
if (category == CATEGORY_GLOBAL)
globalVolume = value;
else
volumes[category] = value;
var p:Playing;
for each (p in playing)
{
if (category == CATEGORY_GLOBAL || p.clip.category == category)
{
p.channel.soundTransform = getST(p.clip);
}
}
}
private static function loopBGM (clip:SoundClip) : void
{
play(clip, int.MAX_VALUE);
}
private static var lastFrame:Number = 0;
private static var count:Number = 0;
private static var sum:Number = 0;
private static function introChecks (e:*) : void
{
// Calculate framerate
var thisFrame:Number = getTimer();
if (count < 6) count ++;
sum = ( (sum * (count - 1)) + (thisFrame - lastFrame)) / count;
lastFrame = thisFrame;
var i:*;
var sc:SoundChannel;
var clip:SoundClip;
for (i in introChannelToClip)
{
sc = i as SoundChannel;
if ( (introChannelToClip[sc] as SoundClip).source.length - sc.position * 1.002 < 2 * sum)
{
loopBGM(introChannelToClip[sc]);
delete introChannelToClip[sc];
}
}
}
private static function play (clip:SoundClip, loops:int = 0) : void
{
var p:Playing = new Playing();
p.clip = clip;
// play the sound actual
p.channel = clip.source.play(
((loops == 0) ? 0 : clip.loopTime),
loops,
getST(clip)
);
// add the completion detect event
if (loops == 0) p.channel.addEventListener(Event.SOUND_COMPLETE, soundComplete);
// add to playing list for volume and count functions
playing.push(p);
// perform count check
// if there is a limit
if (clip.count > 0)
{
// iterate through the playing list
// (ignoring the newly added instance)
var count:int = clip.count-1;
var i:int;
for (i = playing.length-2; i>=0; --i)
{
p = playing[i];
if (p.clip == clip)
{
if (count > 0)
count --;
else
{
// kill and remove the instance
p.channel.stop();
playing.splice(i, 1);
}
}
}
}
}
// find and remove the finished sound from the playing list
private static function soundComplete (e:Event)
{
var p:Playing;
var i:int;
var c:int = playing.length
for (i=0; i<c; ++i)
{
p = playing[i];
if (p.channel == e.target)
{
playing.splice(i, 1);
return;
}
}
}
// get SoundTransform for the specified clip
private static function getST (clip:SoundClip) : SoundTransform
{
var volume:Number = globalVolume * clip.volume * (volumes[clip.category] as Number);
return new SoundTransform(volume);
}
}
}
import flash.media.Sound;
import flash.media.SoundChannel;
class SoundClip
{
public var source:Sound;
public var label:*;
public var category:*;
public var volume:Number;
public var count:int;
public var loopTime:Number = 0;
}
class Playing
{
public var clip:SoundClip;
public var channel:SoundChannel;
}