[System] Simulate 3D Sound

Scripts

13 years
edited 8 years
This library will allow you to simulate a 3D sound. It will have most of the features of a 3D sound, except for cones and velocity. Most of these are guesstimates, but when I compared it to the regular 3D sounds, they were very similar.

I don't have a 100% concrete grasp on what each field does, so if you have more insight on it, feel free to share. I interpreted it from a lot of testing, but even then there are still a few things I can't account for.

Anyway, why have this system? (1) 3D sounds are incredibly annoying. As far as I can tell, mp3's and 2-channel sounds don't work as them. This will allow you to use it as a regular sound, so you won't have to worry about any of that. (2) If you have a 3D sound, you cannot use SetSoundPlayPosition(...). If your sound is not 3D, you cannot have 3D features (obviously). This system will serve as a compromise, having the sound as non-3D and emulating the features of 3D sounds to get the best aspects of both sides. 

Enjoy.

Code (jass) Select
1
library Simulate3DSound /* v.1.0.1.1
2
*********************************************************************
3
*
4
*   Simulates a 3D Sound using a regular sound. This extends the
5
*   functionality for regular sounds, allowing you to apply 3D 
6
*   sound effects and positioning. In addition, you can use the
7
*   sound functions that require the sound to be normal (non-3D).
8
*
9
*   This will allow you to use sounds that normally wouldn't work
10
*   as 3D (for example, 2-channel wav).
11
*
12
*********************************************************************
13
*
14
*   */uses/*
15
*   
16
*       */ TimerUtils /*    [url]http://www.wc3c.net/showthread.php?t=101322[/url]
17
*
18
********************************************************************
19
*
20
*   Description of Arguments
21
*
22
*       sound s
23
*
24
*           - The base sound to be played. The function will play 
25
*             the sound for you. This sound must already exist as 
26
*             an object (you have to create it). 
27
* 
28
*       unit u
29
*       
30
*           - The sound will follow this unit around. The closer
31
*             the camera is to this unit, the louder the sound.
32
*
33
*       real x, y
34
*
35
*           - The sound will project from this point. The volume
36
*             is loudest within the point's area. 
37
*
38
*       real minD (minimum distance)
39
*
40
*           - Determines how far the camera can go without attenuating
41
*             the volume. From the origin to this distance, the sound's
42
*             volume will be 100%. 
43
*
44
*       real maxD (maximum distance)
45
*
46
*           - Once the camera is past the minimum distance, it will
47
*             gradually reduce until it hits the maximum distance. 
48
*             Once it is at the maximum distance or beyond, it will
49
*             no longer lose volume unless it hits the distance cutoff.
50
*
51
*       real distCutoff (distance cutoff)
52
*
53
*           - The sound will fade to 0% volume once it hits this distance.
54
*             If the camera is farther than "distCutoff" when the sound
55
*             is played, the sound will not be heard by that player. 
56
*
57
********************************************************************
58
*
59
*   Functions
60
*
61
*       function BindSoundToUnit takes sound s, unit u, real minD, real maxD, real distCut returns Sim3DSound
62
*           - Attaches a regular sound to a unit. 
63
*       function BindSoundToPoint takes sound s, real x, real y, real minD, real maxD, real distCut returns Sim3DSound
64
*           - Attaches a regular sound to a point. 
65
*       function SetSoundUnit takes Sim3DSound s, unit u returns nothing
66
*           - Allows you to switch the unit the sound is projecting from. 
67
*       function SetSoundCoordinates takes Sim3DSound s, real x, real y returns nothing
68
*           - Allows you to move the sound to another point. 
69
*       function BindSoundToUnitForPlayer takes player p, sound s, unit u, real minD, real maxD, real distCut returns Sim3DSound
70
*       function BindSoundToPointForPlayer takes player p, sound s, real x, real y, real minD, real maxD, real distCut returns Sim3DSound
71
*
72
********************************************************************
73
*
74
*   Bugs
75
*
76
*       This doesn't behave 100% like Warcraft 3 3D sounds. It does
77
*       not increase volume in relation to zoom (I may add that feature
78
*       in the future), and the "minimum distance" does not behave
79
*       exactly like Warcraft 3's. I'm still trying to figure it out. 
80
*       However, it is realistic and works well. 
81
*
82
********************************************************************/
83
84
    globals
85
        // Determines how often the volume is updated
86
        private constant real PERIOD = 0.2
87
    endglobals
88
89
    private struct Sim3DSound
90
        private sound snd
91
        private unit source
92
        private real sourceX
93
        private real sourceY
94
        private boolean adjust
95
        
96
        private real initial
97
        private real factor
98
        private real dur
99
        private real maxD
100
        private real distCut
101
        
102
        private static method expire takes nothing returns nothing
103
            local thistype this = GetTimerData(GetExpiredTimer())
104
            local real dx  
105
            local real dy            
106
            local real dist
107
            local integer volume = 0
108
            
109
            if this.source == null then
110
                set dx = this.sourceX - GetCameraTargetPositionX()
111
                set dy = this.sourceY - GetCameraTargetPositionY()
112
            else
113
                set dx = GetUnitX(this.source) - GetCameraTargetPositionX()
114
                set dy = GetUnitY(this.source) - GetCameraTargetPositionY()
115
            endif
116
            set dist = SquareRoot(dx * dx + dy * dy)
117
            
118
            if this.adjust then
119
                if dist > maxD and dist < distCut then
120
                    call SetSoundVolume(snd, R2I(this.initial - maxD * this.factor))
121
                else
122
                    set volume = R2I(this.initial - dist * this.factor) // linear reduction
123
                    if volume < 0 then
124
                        call SetSoundVolume(snd, 1)
125
                    else
126
                        call SetSoundVolume(snd, volume)
127
                    endif
128
                endif
129
            endif
130
            if this.dur <= 0 then
131
                call ReleaseTimer(GetExpiredTimer())
132
            endif
133
            set this.dur = this.dur - PERIOD
134
        endmethod
135
        
136
        method setSoundPosition takes real x, real y returns nothing
137
            set this.sourceX = x
138
            set this.sourceY = y
139
        endmethod
140
        
141
        method setSource takes unit u returns nothing
142
            set this.source = u
143
        endmethod
144
        
145
        static method createOnPoint takes sound s, real x, real y, real minDist, real maxDist, real distCutoff returns thistype
146
            local thistype this = thistype.create()
147
            local real dx = x - GetCameraTargetPositionX()
148
            local real dy = y - GetCameraTargetPositionY()
149
            local real dist = dx*dx + dy*dy
150
            
151
            set this.snd = s
152
            set this.source = null
153
            set this.sourceX = x
154
            set this.sourceY = y
155
            set this.distCut = distCutoff
156
            set this.factor = 127 / distCutoff
157
            set this.initial = 127 + minDist * this.factor
158
            set this.maxD = maxDist
159
            set this.dur = GetSoundDuration(s) * 0.001
160
            set this.adjust = dist < distCutoff * distCutoff
161
            if not this.adjust then
162
                call SetSoundVolume(s, 1)
163
            endif
164
            
165
            call TimerStart(NewTimerEx(this), PERIOD, true, function thistype.expire)
166
            return this
167
        endmethod
168
        
169
        static method attachToUnit takes sound s, unit u, real minDist, real maxDist, real distCutoff returns thistype
170
            local thistype this = thistype.create()
171
            local real dx = GetUnitX(u) - GetCameraTargetPositionX()
172
            local real dy = GetUnitY(u) - GetCameraTargetPositionY()
173
            local real dist = dx*dx + dy*dy
174
            
175
            set this.snd = s
176
            set this.source = u
177
            set this.distCut = distCutoff
178
            set this.factor = 127 / distCutoff
179
            set this.initial = 127 + minDist * this.factor
180
            set this.maxD = maxDist
181
            set this.dur = GetSoundDuration(s) * 0.001
182
            set this.adjust = dist < distCutoff * distCutoff
183
            if not this.adjust then
184
                call SetSoundVolume(s, 1)
185
            endif
186
            
187
            call TimerStart(NewTimerEx(this), PERIOD, true, function thistype.expire)
188
            return this
189
        endmethod
190
    endstruct
191
    
192
    function BindSoundToUnit takes sound s, unit u, real minD, real maxD, real distCut returns Sim3DSound
193
        if not GetSoundIsPlaying(s) then
194
            call StartSound(s)
195
        endif
196
        return Sim3DSound.attachToUnit(s, u, minD, maxD, distCut)
197
    endfunction
198
    
199
    function BindSoundToPoint takes sound s, real x, real y, real minD, real maxD, real distCut returns Sim3DSound
200
        if not GetSoundIsPlaying(s) then
201
            call StartSound(s)
202
        endif
203
        return Sim3DSound.createOnPoint(s, x, y, minD, maxD, distCut)
204
    endfunction
205
    
206
    function SetSoundUnit takes Sim3DSound s, unit u returns nothing
207
        call s.setSource(u)
208
    endfunction
209
    
210
    function SetSoundCoordinates takes Sim3DSound s, real x, real y returns nothing
211
        call s.setSoundPosition(x, y)
212
    endfunction
213
    
214
    function BindSoundToUnitForPlayer takes player p, sound s, unit u, real minD, real maxD, real distCut returns Sim3DSound
215
        if GetLocalPlayer() == p then
216
            if not GetSoundIsPlaying(s) then
217
                call StartSound(s)
218
            endif
219
        endif
220
        return Sim3DSound.attachToUnit(s, u, minD, maxD, distCut)
221
    endfunction
222
    
223
    function BindSoundToPointForPlayer takes player p, sound s, real x, real y, real minD, real maxD, real distCut returns Sim3DSound
224
        if GetLocalPlayer() == p then
225
            if not GetSoundIsPlaying(s) then
226
                call StartSound(s)
227
            endif
228
        endif
229
        return Sim3DSound.createOnPoint(s, x, y, minD, maxD, distCut)
230
    endfunction
231
    
232
endlibrary


EDIT: Updated with test map and support for SoundTools. (just use call BindSound...(soundObject.run()) or whatever the method is.
13 years
WOAHHH!!! man!!! this is awesome!!! just one thing. I consider to add a test map and with it an example about how to use it.
13 years
I added a test map. Although, I didn't really know what to explain. The test map is really hastily-made though, nothing fancy. You can play the sound, then you can move your camera away and watch the system work its magic. That is about it.

I added one example in GUI-jass and one in vJASS.
13 years
Great!!! That's what I call a nice resource. In fact this baby have some nice purpose in a secret project of mine :)
13 years
This seems like a nice resource.

From what I can see, this should work well in multiplayer since no local factors will lead to any variations in the local handle and string tables.

Approved.
13 years
These are what the sound fields are supposed to represent:

Min Distance - The sound level at the minimum distance that the sound can be heard.
Max Distance - The sound level at the maximum distance that the sound can be heard.
Distance Cutoff - The distance at which the sound will begin to fade out.

I found it in the readme for the sound editor. Oops. Anyway, I may update this eventually to mimic it more accurately now that I know the proper definitions, but for now this system should come pretty close (or at least, it should be able to offer similar behavior).
13 years
Purgeandfire rank: newbie
wat?
give him epic guy rank (?)
13 years
Thanks darkly. :)

Bugfix. I was referring to this.source when it was null. Sorry to anyone who had issues with it.