[System] SoundTools

Scripts

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.

Code (jass) Select
1
/***********************************************
2
*
3
*   SoundTools
4
*   v2.3.0.0
5
*   By Magtheridon96
6
*
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_RANGE
17
*       constant integer DEFAULT_SOUND_FADE_IN_RATE
18
*       constant integer DEFAULT_SOUND_FADE_OUT_RATE
19
*       constant string  DEFAULT_SOUND_EAX_SETTINGS
20
*       constant integer DEFAULT_SOUND_VOLUME
21
*       constant integer DEFAULT_SOUND_PITCH
22
*
23
*
24
*       struct Sound extends array
25
*
26
*           readonly string file
27
*           readonly integer duration
28
*           readonly boolean looping
29
*           readonly boolean is3D
30
*           readonly boolean stopOnLeaveRange
31
*           readonly integer fadeIn
32
*           readonly integer fadeOut
33
*           readonly string eaxSetting
34
*
35
*           static method create takes string fileName, integer duration, boolean looping, boolean is3D returns thistype
36
*               - 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 thistype
38
*               - 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 boolean
40
*               - Releases a sound and throws it into the recycler. Also stops the sound.
41
*
42
*           static method getTotalInstances takes nothing returns integer
43
*               - Determines the total number of sounds playing.
44
*           method getInstances takes nothing returns integer
45
*               - Determines the total number of sounds of a certain type that are running.
46
*
47
*           method run takes nothing returns sound
48
*               - Plays the sound.
49
*           method runEx takes integer volume, real pitch returns sound
50
*               - Plays the sound. This function allows you to pass in extra arguments.
51
*
52
*           method runUnit takes unit whichUnit returns sound
53
*               - Plays the sound on a unit.
54
*           method runPoint takes real x, real y, real z returns sound
55
*               - Plays the sound at a point.
56
*           method runPlayer takes player whichPlayer returns sound
57
*               - Plays the sound for a player.
58
*
59
*           method runUnitEx takes unit whichUnit, integer volume, real pitch returns sound
60
*               - 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 sound
62
*               - 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 sound
64
*               - 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 Sound
67
*           - 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 Sound
69
*           - 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 integer
71
*           - Determines the total number of sounds playing.
72
*       function GetSoundInstances takes Sound this returns integer
73
*           - Determines the total number of sounds of a certain type that are running.
74
*       function ReleaseSound takes sound s returns boolean
75
*           - Releases a sound and throws it into the recycler. Also stops the sound.
76
*       function RunSound takes Sound this returns sound
77
*           - Plays the sound.
78
*       function RunSoundEx takes Sound this, integer volume, real pitch returns sound
79
*           - Plays the sound. This function allows you to pass in extra arguments.
80
*       function RunSoundOnUnit takes Sound this, unit whichUnit returns sound
81
*           - Plays the sound on a unit.
82
*       function RunSoundAtPoint takes Sound this, real x, real y, real z returns sound
83
*           - Plays the sound at a point.
84
*       function RunSoundForPlayer takes Sound this, player p returns sound
85
*           - Plays the sound for a player.
86
*       function RunSoundOnUnitEx takes Sound this, unit whichUnit, integer volume, real pitch returns sound
87
*           - 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 sound
89
*           - 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 sound
91
*           - 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
    globals
101
        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
    endglobals
108
    
109
    globals
110
        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
    endglobals
115
    
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
        endmethod
158
        
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
        endmethod
162
        
163
        static method getTotalInstances takes nothing returns integer
164
            return totalPlaying
165
        endmethod
166
        
167
        method getInstances takes nothing returns integer
168
            return playing[this]
169
        endmethod
170
        
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
        endmethod
193
        
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
            loop
204
                exitwhen this == 0
205
                
206
                /*
207
                *   Play the sound
208
                */
209
                call StartSound(media[this])
210
                
211
                /*
212
                *   If the sound is not looping, we recycle
213
                *   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
                endif
221
                
222
                set media[this] = null
223
                set this = next[this]
224
            endloop
225
            
226
            /*
227
            *   Fast stack destruction.
228
            */
229
            set next[0] = 0
230
            
231
            set t = null
232
        endmethod
233
        
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
            endif
241
            
242
            /*
243
            *   This prevents sounds of the same type from being
244
            *   played in the same thread. This is good for
245
            *   performance because playing the same sound-type
246
            *   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 instance
267
                    */
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 channel
273
                    */
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
                    endif
285
                else
286
                    /*
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
                endif
292
            debug else
293
                debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[SoundTools]Warning: Attempted to run the same sound twice.")
294
            endif
295
            
296
            return media[this]
297
        endmethod
298
        
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
            endif
314
            
315
            return snd
316
        endmethod
317
        
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
            endif
333
            
334
            return snd
335
        endmethod
336
        
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
            endif
348
            
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
        endmethod
361
        
362
        method runUnit takes unit whichUnit returns sound
363
            set snd = this.run()
364
            call AttachSoundToUnit(snd, whichUnit)
365
            return snd
366
        endmethod
367
        
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 snd
372
        endmethod
373
        
374
        method runPlayer takes player p returns sound
375
            set snd = this.run()
376
            if GetLocalPlayer() != p then
377
                call SetSoundVolume(snd, 0)
378
            endif
379
            return snd
380
        endmethod
381
        
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 snd
386
        endmethod
387
        
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 snd
392
        endmethod
393
        
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
            endif
399
            return snd
400
        endmethod
401
        
402
    endstruct
403
    
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
    endfunction
407
    
408
    function NewSound takes string fileName, integer duration, boolean looping, boolean is3D returns Sound
409
        return Sound.create(fileName, duration, looping, is3D)
410
    endfunction
411
    
412
    function GetTotalSoundInstances takes nothing returns integer
413
        return Sound.getTotalInstances()
414
    endfunction
415
    
416
    function GetSoundInstances takes Sound this returns integer
417
        return this.getInstances()
418
    endfunction
419
    
420
    function RunSound takes Sound this returns sound
421
        return this.run()
422
    endfunction
423
    
424
    function RunSoundEx takes Sound this, integer volume, real pitch returns sound
425
        return this.runEx(volume, pitch)
426
    endfunction
427
    
428
    function ReleaseSound takes sound s returns boolean
429
        return Sound.release(s)
430
    endfunction
431
    
432
    function RunSoundOnUnit takes Sound this, unit whichUnit returns sound
433
        return this.runUnit(whichUnit)
434
    endfunction
435
    
436
    function RunSoundAtPoint takes Sound this, real x, real y, real z returns sound
437
        return this.runPoint(x, y, z)
438
    endfunction
439
    
440
    function RunSoundForPlayer takes Sound this, player p returns sound
441
        return this.runPlayer(p)
442
    endfunction
443
    
444
    function RunSoundOnUnitEx takes Sound this, unit whichUnit, integer volume, real pitch returns sound
445
        return this.runUnitEx(whichUnit, volume, pitch)
446
    endfunction
447
    
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
    endfunction
451
    
452
    function RunSoundForPlayerEx takes Sound this, player p, integer volume, real pitch returns sound
453
        return this.runPlayerEx(p, volume, pitch)
454
    endfunction
455
    
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.