-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathAudio.as
235 lines (189 loc) · 8.57 KB
/
Audio.as
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
package
{
import flash.events.Event;
import flash.net.*;
import flash.media.Sound;
import flash.media.SoundChannel;
import flash.media.SoundTransform;
import flash.geom.Point;
// parameters that could be added
// retrigger : when called a 2nd time while already playing : restart (stops current), ignore (let current play), overlap (start multiple instances)
// memory : resident (load at startup), demand (load when needed and keep), purge (on complete)
// ignore delay (seconds) -- don't play again for x seconds
public class Audio
{
// public static, so it can be accessed from anywhere as "Audio.ready"
// will become true when config and all resident sounds are loaded. false until then.
// the program should not start until ready is true.
// Audio.play will be ignored until this is true. (ensure background sound isn't started until this is true!)
public static var ready : Boolean = false;
// config holds configuration for the audio parameters
// public static, so it can be accessed from anywhere as "Audio.config"
// NOTE this null until loaded
public static var config : XML;
// private static, "Audio.instance" can be addressed by static functions within Audio
private static var instance : Audio;
// variable to hold the object that loads the xml data.
// this object has a temporary life - long enough to load the XML data, before it is nulled out
private var audioConfigLoader : URLLoader;
// associative array - string name of sample is index, holds XML data for the sample
private var sampleConfig : Array = new Array;
// associative array - string name of sample is index, holds sound data object
private var sampleAudio : Array = new Array;
//The channel for every triggered sound effect gets pushed into an array so that they can be stopped instantly
//When the player presses the mute button
private static var soundList: Array = new Array();
// create the audio manager object
// this follows a "Singleton" pattern
// audio object is created once & accessible globally in the program
public static function init()
{
if (!instance) instance = new Audio();
}
// create the audio manager object
// this follows a "Singleton" pattern
// audio object is created once & accessible globally in the program
public static function play(name : String, numVariations = 0)
{
//If the game is muted and it wasn't a song that was triggered, return
if (Config.muteSounds && name != "music") return;
//If the music is muted and it was a song that was triggered, return
if (Config.muteMusic && name == "music") return;
var r = int(Math.random()*numVariations);
name = name + "_" + r;
if (!instance) return;
if (!instance.sampleConfig) return;
if (!instance.sampleAudio) return;
var sound = instance.sampleAudio[name];
if (!sound) {trace ("NO SOUND NAMED >>" + name + "<<"); return; }
var volume : Number = instance.sampleConfig[name].volume;
var repeats : int = (instance.sampleConfig[name].type == "loop") ? int.MAX_VALUE : 0; // looping sound plays 2 billion times, otherwise once
var startTime : Number = 0;
var channel = sound.play(startTime, repeats, new SoundTransform(volume,0));
if (name != "music_0") soundList.push(channel); //Push the sound channel into an array so we can stop them immediately when the player mutes the game
return channel;
}
//Take a point to the player and the sound source, apply a falloff to the volume and pan the audio based on the distance and direction
public static function playDynamic(name : String, player:Point, source:Point, maxVolumeDistance = 1600, maxPanDistance = 800, numVariations = 0)
{
//If the game is muted, return
if (Config.muteSounds) return;
var r = int(Math.random()*numVariations);
name = name + "_" + r;
if (!instance) return;
if (!instance.sampleConfig) return;
if (!instance.sampleAudio) return;
var sound = instance.sampleAudio[name];
if (!sound) {trace ("NO SOUND NAMED >>" + name + "<<"); return; }
var point1:Point = new Point(player.x, player.y);
var point2:Point = new Point(source.x, source.y);
var distance = Point.distance(point1, point2);
var volumeDistance = distance;
if (volumeDistance > maxVolumeDistance) volumeDistance = maxVolumeDistance;
var volume = 1 - (volumeDistance / maxVolumeDistance);
//trace("Volume: " + volume);
//trace(volume);
if (point1.x > point2.x) distance = -distance;
var pan = distance / maxPanDistance;
//trace ("Pan: " +pan);
volume *= instance.sampleConfig[name].volume;
var repeats : int = (instance.sampleConfig[name].type == "loop") ? int.MAX_VALUE : 0; // looping sound plays 2 billion times, otherwise once
var startTime : Number = 0;
var channel = sound.play(startTime, repeats, new SoundTransform(volume,pan));
soundList.push(channel); //Push the sound channel into an array so we can stop them immediately when the player mutes the game
return channel;
}
// will stop a sound channel returned from play function
public static function stop(channel : SoundChannel)
{
if (channel == null) return;
channel.stop();
}
//Stop all the playing sound effects immediately when the player mutes the game
public static function stopAll()
{
for (var i:int = 0; i < soundList.length; i++)
{
//Stop every sound effect channel
soundList[i].stop();
//Set it to null for the garbage collector
soundList[i] = null;
}
//Clear the array that is now full of null pointers
soundList = new Array();
}
//
// create an instance of our Audio manager
// it automatically loads config when created
// and automatically loads resident (generally small) sound effects - e.g. weapon fire and explosions.
// large sounds (e.g. background music) should be loaded by the levels / screens that use them
//
public function Audio()
{
loadAudioConfig();
}
// start the load of config data
private function loadAudioConfig()
{
trace ("loading audio config");
audioConfigLoader = new URLLoader();
if (Main.loadFromNet) audioConfigLoader.load(new URLRequest("https://dl.dropboxusercontent.com/s/55g755xojlq0z2n/audio.xml?token_hash=AAGzVcD4PLaoAnQN8yhe0o1uKgRJ7-KA91hk5_0x3JprfQ&dl=1"));
//if (!Main.loadFromNet) audioConfigLoader.load(new URLRequest("data/audio.xml"));
audioConfigLoader.addEventListener(Event.COMPLETE, loadAudioConfigDone);
}
// load is completed -
// NOTE - when running on a local PC, this will generally be instant
// if loading from the internet, it may be numerious frames before this completes
// so it's important to check Audio.configXML is not null before attempting to use it
// (shouldn't really start the main menu until it is loaded)
private function loadAudioConfigDone(e :Event)
{
// import the loaded XML into our config variable for later reference
trace ("AUDIO CONFIG LOADED vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv");
config = new XML(e.target.data);
trace (config);
trace ("AUDIO CONFIG LOADED ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
// and clean up the loader
audioConfigLoader.removeEventListener(Event.COMPLETE, loadAudioConfigDone);
audioConfigLoader = null;
// now load resident sounds
loadResidentAudio();
}
// any audio files listed as resident in the config file should be loaded immediately
private function loadResidentAudio()
{
// record all sample info
for (var i : int = 0; i < config.samples.sample.length(); ++i)
{
var thisSample = config.samples.sample[i]; // pointer into the xml fragment for neater code
sampleConfig[thisSample.name] = thisSample;
}
// now load the actual samples (where desired)
for (var key in sampleConfig)
{
trace (key + " :: " + sampleConfig[key].file + " VOL:" + sampleConfig[key].volume);
if (Main.loadFromNet) var path = sampleConfig[key].url;
//if (!Main.loadFromNet) var path = "data/audio/" + sampleConfig[key].file;
var sound = new Sound();
loadSoundFromFile(sound, path);
sampleAudio[key] = sound; // NB, it's still loading, but it will load to here
}
// audio system is ready now (strictly shouldn't do this until all are loaded, but flash will play when load finishes)
ready = true;
}
//
//
// Loading sound from a file
//
//
private static function loadSoundFromFile(sound : Object, path : String)
{
sound.load(new URLRequest(path));
sound.addEventListener(Event.COMPLETE, soundLoaded);
}
private static function soundLoaded(e : Event)
{
e.target.removeEventListener(Event.COMPLETE, soundLoaded);
}
}
}