14 years
edited 8 years
This system allows you to play a sound immediately after creating it (Which is impossible inside Warcraft III)
Basically, all what it does is start a timer and play the sound.
It also recycles these sounds to decrease the amount of RAM used up.
Rising_Dusk's SoundUtils is the original version of this.
This is a more optimal and cleaner version that's easier to read and understand.
Feel free to comment.
Basically, all what it does is start a timer and play the sound.
It also recycles these sounds to decrease the amount of RAM used up.
1 /***********************************************2 *3 * SoundTools4 * v2.3.0.05 * By Magtheridon966 *7 * (Special Thanks to Rising_Dusk for SoundUtils)8 *9 * - Allows you to play sounds immediately after creating them.10 * - Uses a sound recycler to increase efficiency and save RAM.11 *12 * API:13 * ----14 *15 *16 * constant boolean DEFAULT_SOUND_STOPS_ON_LEAVE_RANGE17 * constant integer DEFAULT_SOUND_FADE_IN_RATE18 * constant integer DEFAULT_SOUND_FADE_OUT_RATE19 * constant string DEFAULT_SOUND_EAX_SETTINGS20 * constant integer DEFAULT_SOUND_VOLUME21 * constant integer DEFAULT_SOUND_PITCH22 *23 *24 * struct Sound extends array25 *26 * readonly string file27 * readonly integer duration28 * readonly boolean looping29 * readonly boolean is3D30 * readonly boolean stopOnLeaveRange31 * readonly integer fadeIn32 * readonly integer fadeOut33 * readonly string eaxSetting34 *35 * static method create takes string fileName, integer duration, boolean looping, boolean is3D returns thistype36 * - Creates a sound struct given the filepath, the duration in milliseconds, whether it is looping or not, and whether it is 3D or not.37 * static method createEx takes string fileName, integer duration, boolean looping, boolean is3D, boolean stopOnExitRange, integer fadeIn, integer fadeOut, string eaxSetting returns thistype38 * - In addition to static method create, this allows you to specificy whether the sound stops when the player leaves range, the fadeIn/fadeOut rates and the EAX Setting.39 * static method release takes sound s returns boolean40 * - Releases a sound and throws it into the recycler. Also stops the sound.41 *42 * static method getTotalInstances takes nothing returns integer43 * - Determines the total number of sounds playing.44 * method getInstances takes nothing returns integer45 * - Determines the total number of sounds of a certain type that are running.46 *47 * method run takes nothing returns sound48 * - Plays the sound.49 * method runEx takes integer volume, real pitch returns sound50 * - Plays the sound. This function allows you to pass in extra arguments.51 *52 * method runUnit takes unit whichUnit returns sound53 * - Plays the sound on a unit.54 * method runPoint takes real x, real y, real z returns sound55 * - Plays the sound at a point.56 * method runPlayer takes player whichPlayer returns sound57 * - Plays the sound for a player.58 *59 * method runUnitEx takes unit whichUnit, integer volume, real pitch returns sound60 * - Plays the sound on a unit. This function allows you to pass in extra arguments.61 * method runPointEx takes real x, real y, real z, integer volume, real pitch returns sound62 * - Plays the sound at a point. This function allows you to pass in extra arguments.63 * method runPlayerEx takes player whichPlayer, integer volume, real pitch returns sound64 * - Plays the sound for a player. This function allows you to pass in extra arguments.65 *66 * function NewSound takes string fileName, integer duration, boolean looping, boolean is3D returns Sound67 * - Creates a sound struct given the filepath, the duration in milliseconds, whether it is looping or not, and whether it is 3D or not.68 * function NewSoundEx takes string fileName, integer duration, boolean looping, boolean is3D, boolean stop, integer fadeInRate, integer fadeOutRate, string eax returns Sound69 * - In addition to static method create, this allows you to specificy whether the sound stops when the player leaves range, the fadeIn/fadeOut rates and the EAX Setting.70 * function GetTotalSoundInstances takes nothing returns integer71 * - Determines the total number of sounds playing.72 * function GetSoundInstances takes Sound this returns integer73 * - Determines the total number of sounds of a certain type that are running.74 * function ReleaseSound takes sound s returns boolean75 * - Releases a sound and throws it into the recycler. Also stops the sound.76 * function RunSound takes Sound this returns sound77 * - Plays the sound.78 * function RunSoundEx takes Sound this, integer volume, real pitch returns sound79 * - Plays the sound. This function allows you to pass in extra arguments.80 * function RunSoundOnUnit takes Sound this, unit whichUnit returns sound81 * - Plays the sound on a unit.82 * function RunSoundAtPoint takes Sound this, real x, real y, real z returns sound83 * - Plays the sound at a point.84 * function RunSoundForPlayer takes Sound this, player p returns sound85 * - Plays the sound for a player.86 * function RunSoundOnUnitEx takes Sound this, unit whichUnit, integer volume, real pitch returns sound87 * - Plays the sound on a unit. This function allows you to pass in extra arguments.88 * function RunSoundAtPointEx takes Sound this, real x, real y, real z, integer volume, real pitch returns sound89 * - Plays the sound at a point. This function allows you to pass in extra arguments.90 * function RunSoundForPlayerEx takes Sound this, player p, integer volume, real pitch returns sound91 * - Plays the sound for a player. This function allows you to pass in extra arguments.92 *93 ***********************************************/94 library SoundTools requires Table, TimerUtils
95
96 /*97 * Configuration.98 */99
100 globals101 constant boolean DEFAULT_SOUND_STOPS_ON_LEAVE_RANGE = true
102 constant integer DEFAULT_SOUND_FADE_IN_RATE = 10
103 constant integer DEFAULT_SOUND_FADE_OUT_RATE = 10
104 constant string DEFAULT_SOUND_EAX_SETTINGS = "CombatSoundsEAX"
105 constant integer DEFAULT_SOUND_VOLUME = 127
106 constant integer DEFAULT_SOUND_PITCH = 1
107 endglobals108
109 globals110 private constant integer SOUND_CHANNEL = 5
111 private constant integer SOUND_MIN_DIST = 600
112 private constant integer SOUND_MAX_DIST = 10000
113 private constant integer SOUND_DIST_CUT = 3000
114 endglobals115
116 /*117 * Configuration Ends here.118 */119
120 struct Sound extends array
121 private static key tbK
122 private static key ptK
123 private static Table tb = tbK
124 private static Table pt = ptK
125 private static integer index = 1
126
127 private static Table array stack
128 private static integer array count
129 private static integer array playing
130 private static sound snd = null
131 private static integer totalPlaying = 0
132
133 readonly string file
134 readonly integer duration
135 readonly boolean looping
136 readonly boolean is3D
137 readonly boolean stopOnLeaveRange
138 readonly integer fadeIn
139 readonly integer fadeOut
140 readonly string eaxSetting
141
142 static method createEx takes string fileName, integer dur, boolean loopng, boolean isTD, boolean stop, integer fadeInRate, integer fadeOutRate, string eax returns thistype
143 local thistype this = index
144 set index = index + 1
145
146 set this.file = fileName
147 set this.duration = dur
148 set this.looping = loopng
149 set this.is3D = isTD
150 set this.stopOnLeaveRange = stop
151 set this.fadeIn = fadeInRate
152 set this.fadeOut = fadeOutRate
153 set this.eaxSetting = eax
154
155 set stack[this] = Table.create()
156 return this
157 endmethod158
159 static method create takes string fileName, integer dur, boolean loopng, boolean isTD returns thistype
160 return createEx(fileName, dur, loopng, isTD, DEFAULT_SOUND_STOPS_ON_LEAVE_RANGE, DEFAULT_SOUND_FADE_IN_RATE, DEFAULT_SOUND_FADE_OUT_RATE, DEFAULT_SOUND_EAX_SETTINGS)
161 endmethod162
163 static method getTotalInstances takes nothing returns integer
164 return totalPlaying165 endmethod166
167 method getInstances takes nothing returns integer
168 return playing[this]
169 endmethod170
171 private static method recycle takes nothing returns nothing
172 local timer t = GetExpiredTimer()
173 local sound s = tb.sound[GetHandleId(t)]
174 local integer this = GetTimerData(t)
175
176 /*177 * Stop the sound and push it to the stack.178 */179 call StopSound(s, false, true)
180 set stack[this].sound[count[this]] = s
181 set count[this] = count[this] + 1
182
183 set totalPlaying = totalPlaying - 1
184 set playing[this] = playing[this] - 1
185
186 /*187 * Null locals and release timer.188 */189 call ReleaseTimer(t)
190 set t = null
191 set s = null
192 endmethod193
194 private static integer array next
195 private static sound array media
196
197 private static method runSounds takes nothing returns nothing
198 local thistype this = next[0]
199 local timer t
200
201 call ReleaseTimer(GetExpiredTimer())
202
203 loop204 exitwhen this == 0
205
206 /*207 * Play the sound208 */209 call StartSound(media[this])
210
211 /*212 * If the sound is not looping, we recycle213 * it and push it into the stack after it 214 * finishes playing.215 */216 if not this.looping then
217 set t = NewTimerEx(this)
218 set tb.sound[GetHandleId(t)] = media[this]
219 call TimerStart(t, this.duration * 0.001, false, function thistype.recycle)
220 endif221
222 set media[this] = null
223 set this = next[this]
224 endloop225
226 /*227 * Fast stack destruction.228 */229 set next[0] = 0
230
231 set t = null
232 endmethod233
234 private method play takes nothing returns sound
235 /*236 * If the run-stack is empty, we start the run-timer.237 */238 if next[0] == 0 then
239 call TimerStart(NewTimer(), 0, false, function thistype.runSounds)
240 endif241
242 /*243 * This prevents sounds of the same type from being244 * played in the same thread. This is good for245 * performance because playing the same sound-type246 * twice at the same time will make no difference.247 */248 if media[this] == null then
249 /*250 * Push instance to run-stack.251 */252 set next[this] = next[0]
253 set next[0] = this
254
255 /*256 * Increase the number of sounds playing.257 */258 set totalPlaying = totalPlaying + 1
259 set playing[this] = playing[this] + 1
260
261 /*262 * We check if the stack is empty.263 */264 if count[this] == 0 then
265 /*266 * Create a new sound and point it to the struct instance267 */268 set media[this] = CreateSound(this.file, this.looping, this.is3D, this.stopOnLeaveRange, this.fadeIn, this.fadeOut, this.eaxSetting)
269 set pt[GetHandleId(media[this])] = this
270
271 /*272 * Set the duration of the sound and the channel273 */274 call SetSoundDuration(media[this], this.duration)
275 call SetSoundChannel(snd, SOUND_CHANNEL)
276
277 /*278 * If the sound is 3D, we configure it some more.279 */280 if this.is3D then
281 call SetSoundDistances(media[this], SOUND_MIN_DIST, SOUND_MAX_DIST)
282 call SetSoundDistanceCutoff(media[this], SOUND_DIST_CUT)
283 call SetSoundConeOrientation(media[this], 0, 0, 0)
284 endif285 else286 /*287 * We have a sound in our stack, so we pop it.288 */289 set count[this] = count[this] - 1
290 return stack[this].sound[count[this]]
291 endif292 debug else
293 debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[SoundTools]Warning: Attempted to run the same sound twice.")
294 endif295
296 return media[this]
297 endmethod298
299 method run takes nothing returns sound
300 set snd = this.play()
301
302 /*303 * Configure volume and pitch to default values.304 */305 call SetSoundVolume(snd, DEFAULT_SOUND_VOLUME)
306 call SetSoundPitch(snd, DEFAULT_SOUND_PITCH)
307
308 /*309 * If the sound is 3D, we configure the cone angle volume.310 */311 if this.is3D then
312 call SetSoundConeAngles(snd, 0, 0, DEFAULT_SOUND_VOLUME)
313 endif314
315 return snd316 endmethod317
318 method runEx takes integer volume, real pitch returns sound
319 set snd = this.play()
320
321 /*322 * Configure volume and pitch.323 */324 call SetSoundVolume(snd, volume)
325 call SetSoundPitch(snd, pitch)
326
327 /*328 * If the sound is 3D, we configure the cone angle volume.329 */330 if this.is3D then
331 call SetSoundConeAngles(snd, 0, 0, volume)
332 endif333
334 return snd335 endmethod336
337 static method release takes sound s returns boolean
338 local integer id = GetHandleId(s)
339 local integer this = pt[id]
340
341 if s == null then
342 debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[SoundTools]Error: Attempted to release a null sound.")
343 return false
344 elseif this == 0 then
345 debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[SoundTools]Error: Attempted to release a sound not allocated by RunSound.")
346 return false
347 endif348
349 /*350 * Stop the sound and push it to the stack.351 */352 call StopSound(s, false, true)
353 set stack[this].sound[count[this]] = s
354 set count[this] = count[this] + 1
355
356 set totalPlaying = totalPlaying - 1
357 set playing[this] = playing[this] - 1
358
359 return true
360 endmethod361
362 method runUnit takes unit whichUnit returns sound
363 set snd = this.run()
364 call AttachSoundToUnit(snd, whichUnit)
365 return snd366 endmethod367
368 method runPoint takes real x, real y, real z returns sound
369 set snd = this.run()
370 call SetSoundPosition(snd, x, y, z)
371 return snd372 endmethod373
374 method runPlayer takes player p returns sound
375 set snd = this.run()
376 if GetLocalPlayer() != p then
377 call SetSoundVolume(snd, 0)
378 endif379 return snd380 endmethod381
382 method runUnitEx takes unit whichUnit, integer volume, real pitch returns sound
383 set snd = this.runEx(volume, pitch)
384 call AttachSoundToUnit(snd, whichUnit)
385 return snd386 endmethod387
388 method runPointEx takes real x, real y, real z, integer volume, real pitch returns sound
389 set snd = this.runEx(volume, pitch)
390 call SetSoundPosition(snd, x, y, z)
391 return snd392 endmethod393
394 method runPlayerEx takes player p, integer volume, real pitch returns sound
395 set snd = this.runEx(volume, pitch)
396 if GetLocalPlayer() != p then
397 call SetSoundVolume(snd, 0)
398 endif399 return snd400 endmethod401
402 endstruct403
404 function NewSoundEx takes string fileName, integer duration, boolean looping, boolean is3D, boolean stop, integer fadeInRate, integer fadeOutRate, string eax returns Sound
405 return Sound.createEx(fileName, duration, looping, is3D, stop, fadeInRate, fadeOutRate, eax)
406 endfunction407
408 function NewSound takes string fileName, integer duration, boolean looping, boolean is3D returns Sound
409 return Sound.create(fileName, duration, looping, is3D)
410 endfunction411
412 function GetTotalSoundInstances takes nothing returns integer
413 return Sound.getTotalInstances()
414 endfunction415
416 function GetSoundInstances takes Sound this returns integer
417 return this.getInstances()
418 endfunction419
420 function RunSound takes Sound this returns sound
421 return this.run()
422 endfunction423
424 function RunSoundEx takes Sound this, integer volume, real pitch returns sound
425 return this.runEx(volume, pitch)
426 endfunction427
428 function ReleaseSound takes sound s returns boolean
429 return Sound.release(s)
430 endfunction431
432 function RunSoundOnUnit takes Sound this, unit whichUnit returns sound
433 return this.runUnit(whichUnit)
434 endfunction435
436 function RunSoundAtPoint takes Sound this, real x, real y, real z returns sound
437 return this.runPoint(x, y, z)
438 endfunction439
440 function RunSoundForPlayer takes Sound this, player p returns sound
441 return this.runPlayer(p)
442 endfunction443
444 function RunSoundOnUnitEx takes Sound this, unit whichUnit, integer volume, real pitch returns sound
445 return this.runUnitEx(whichUnit, volume, pitch)
446 endfunction447
448 function RunSoundAtPointEx takes Sound this, real x, real y, real z, integer volume, real pitch returns sound
449 return this.runPointEx(x, y, z, volume, pitch)
450 endfunction451
452 function RunSoundForPlayerEx takes Sound this, player p, integer volume, real pitch returns sound
453 return this.runPlayerEx(p, volume, pitch)
454 endfunction455
456 endlibrary
Rising_Dusk's SoundUtils is the original version of this.
This is a more optimal and cleaner version that's easier to read and understand.
Feel free to comment.