1 // short living sound occurrence, dies once the sound stops
2
3 #include "cube.h"
4
5 #define DEBUGCOND (audiodebug==1)
6
7 VARP(gainscale, 1, 90, 100);
8 int warn_about_unregistered_sound = 0;
location(int sound,const worldobjreference & r,int priority)9 location::location(int sound, const worldobjreference &r, int priority) : cfg(NULL), src(NULL), ref(NULL), stale(false), playmillis(0)
10 {
11 vector<soundconfig> &sounds = (r.type==worldobjreference::WR_ENTITY ? mapsounds : gamesounds);
12 if(!sounds.inrange(sound))
13 {
14 if (lastmillis - warn_about_unregistered_sound > 30 * 1000) // delay message to every 30 secs so console is not spammed.
15 {
16 // occurs when a map contains an ambient sound entity, but sound entity is not found in map cfg file.
17 conoutf("\f3ERROR: this map contains at least one unregistered ambient sound (sound entity# %d)", sound);
18 warn_about_unregistered_sound = lastmillis;
19 }
20 stale = true;
21 return;
22 }
23
24 // get sound config
25 cfg = &sounds[sound];
26 cfg->onattach();
27 const float dist = camera1->o.dist(r.currentposition());
28 if((r.type==worldobjreference::WR_ENTITY && cfg->maxuses >= 0 && cfg->uses >= cfg->maxuses) || cfg->muted || (cfg->audibleradius && dist>cfg->audibleradius)) // check max-use limits and audible radius
29 {
30 stale = true;
31 return;
32 }
33
34 // assign buffer
35 sbuffer *buf = cfg->buf;
36 if(!buf || !buf->id)
37 {
38 stale = true;
39 return;
40 }
41
42 // obtain source
43 src = sourcescheduler::instance().newsource(priority, r.currentposition());
44 // apply configuration
45 if(!src || !src->valid || !src->buffer(cfg->buf->id) || !src->looping(cfg->loop) || !setvolume(1.0f))
46 {
47 stale = true;
48 return;
49 }
50 src->init(this);
51
52 // set position
53 attachworldobjreference(r);
54 }
55
~location()56 location::~location()
57 {
58 if(src) sourcescheduler::instance().releasesource(src);
59 if(cfg) cfg->ondetach();
60 if(ref)
61 {
62 ref->detach();
63 DELETEP(ref);
64 }
65 }
66
67 // attach a reference to a world object to get the 3D position from
68
attachworldobjreference(const worldobjreference & r)69 void location::attachworldobjreference(const worldobjreference &r)
70 {
71 ASSERT(!stale && src && src->valid);
72 if(stale) return;
73 if(ref)
74 {
75 ref->detach();
76 DELETEP(ref);
77 }
78 ref = r.clone();
79 evaluateworldobjref();
80 ref->attach();
81 }
82
83 // enable/disable distance calculations
evaluateworldobjref()84 void location::evaluateworldobjref()
85 {
86 src->sourcerelative(ref->nodistance());
87 }
88
89 // marks itself for deletion if source got lost
onsourcereassign(source * s)90 void location::onsourcereassign(source *s)
91 {
92 if(s==src)
93 {
94 stale = true;
95 src = NULL;
96 }
97 }
98
updatepos()99 void location::updatepos()
100 {
101 ASSERT(!stale && ref);
102 if(stale) return;
103
104 const vec &pos = ref->currentposition();
105
106 // forced fadeout radius
107 bool volumeadjust = (cfg->model==soundconfig::DM_LINEAR);
108 float forcedvol = 1.0f;
109 if(volumeadjust)
110 {
111 float dist = camera1->o.dist(pos);
112 if(dist>cfg->audibleradius) forcedvol = 0.0f;
113 else if(dist<0) forcedvol = 1.0f;
114 else forcedvol = 1.0f-(dist/cfg->audibleradius);
115 }
116
117 // reference determines the used model
118 switch(ref->type)
119 {
120 case worldobjreference::WR_CAMERA: break;
121 case worldobjreference::WR_PHYSENT:
122 {
123 if(!ref->nodistance()) src->position(pos);
124 if(volumeadjust) setvolume(forcedvol);
125 break;
126 }
127 case worldobjreference::WR_ENTITY:
128 {
129 entityreference &eref = *(entityreference *)ref;
130 const float vol = eref.ent->attr4<=0.0f ? 1.0f : eref.ent->attr4/255.0f;
131 float dist = camera1->o.dist(pos);
132
133 if(ref->nodistance())
134 {
135 // own distance model for entities/mapsounds: linear & clamping
136
137 const float innerradius = float(eref.ent->attr3); // full gain area / size property
138 const float outerradius = float(eref.ent->attr2); // fading gain area / radius property
139
140 if(dist <= innerradius) src->gain(1.0f*vol); // inside full gain area
141 else if(dist <= outerradius) // inside fading gain area
142 {
143 const float fadeoutdistance = outerradius-innerradius;
144 const float fadeout = dist-innerradius;
145 src->gain((1.0f - fadeout/fadeoutdistance)*vol);
146 }
147 else src->gain(0.0f); // outside entity
148 }
149 else
150 {
151 // use openal distance model to make the sound appear from a certain direction (non-ambient)
152 src->position(pos);
153 src->gain(vol);
154 }
155 break;
156 }
157 case worldobjreference::WR_STATICPOS:
158 {
159 if(!ref->nodistance()) src->position(pos);
160 if(volumeadjust) setvolume(forcedvol);
161 break;
162 }
163 }
164 }
165
update()166 void location::update()
167 {
168 if(stale) return;
169
170 switch(src->state())
171 {
172 case AL_PLAYING:
173 updatepos();
174 break;
175 case AL_STOPPED:
176 case AL_PAUSED:
177 case AL_INITIAL:
178 stale = true;
179 DEBUG("location is stale");
180 break;
181 }
182 }
183
play(bool loop)184 void location::play(bool loop)
185 {
186 if(stale) return;
187
188 updatepos();
189 if(loop) src->looping(loop);
190 if(src->play()) playmillis = totalmillis;
191 }
192
pitch(float p)193 void location::pitch(float p)
194 {
195 if(stale) return;
196 src->pitch(p);
197 }
198
setvolume(float v)199 bool location::setvolume(float v)
200 {
201 if(stale) return false;
202 return src->gain(cfg->vol/100.0f*((float)gainscale)/100.0f*v);
203 }
204
offset(float secs)205 void location::offset(float secs)
206 {
207 ASSERT(!stale);
208 if(stale) return;
209 src->secoffset(secs);
210 }
211
offset()212 float location::offset()
213 {
214 ASSERT(!stale);
215 if(stale) return 0.0f;
216 return src->secoffset();
217 }
218
drop()219 void location::drop()
220 {
221 src->stop();
222 stale = true; // drop from collection on next update cycle
223 }
224
225
226 // location collection
227
find(int sound,worldobjreference * ref,const vector<soundconfig> & soundcollection)228 location *locvector::find(int sound, worldobjreference *ref, const vector<soundconfig> &soundcollection /* = gamesounds*/)
229 {
230 if(sound<0 || sound>=soundcollection.length()) return NULL;
231 loopi(ulen) if(buf[i] && !buf[i]->stale)
232 {
233 if(buf[i]->cfg != &soundcollection[sound]) continue; // check if its the same sound
234 if(ref && *buf[i]->ref!=*ref) continue; // optionally check if its the same reference
235 return buf[i]; // found
236 }
237 return NULL;
238 }
239
delete_(int i)240 void locvector::delete_(int i)
241 {
242 delete remove(i);
243 }
244
replaceworldobjreference(const worldobjreference & oldr,const worldobjreference & newr)245 void locvector::replaceworldobjreference(const worldobjreference &oldr, const worldobjreference &newr)
246 {
247 loopv(*this)
248 {
249 location *l = buf[i];
250 if(!l || !l->ref) continue;
251 if(*l->ref==oldr) l->attachworldobjreference(newr);
252 }
253 }
254
255 // update stuff, remove stale data
updatelocations()256 void locvector::updatelocations()
257 {
258 // check if camera carrier changed
259 bool camchanged = false;
260 static physent *lastcamera = NULL;
261 if(lastcamera!=camera1)
262 {
263 if(lastcamera!=NULL) camchanged = true;
264 lastcamera = camera1;
265 }
266
267 // update all locations
268 loopv(*this)
269 {
270 location *l = buf[i];
271 if(!l) continue;
272
273 l->update();
274 if(l->stale) delete_(i--);
275 else if(camchanged) l->evaluateworldobjref(); // cam changed, evaluate world reference again
276 }
277 }
278
279 // force pitch across all locations
forcepitch(float pitch)280 void locvector::forcepitch(float pitch)
281 {
282 loopv(*this)
283 {
284 location *l = buf[i];
285 if(!l) continue;
286 if(l->src && l->src->locked) l->src->pitch(pitch);
287 }
288 }
289
290 // delete all sounds except world-neutral sounds like GUI/notification
deleteworldobjsounds()291 void locvector::deleteworldobjsounds()
292 {
293 loopv(*this)
294 {
295 location *l = buf[i];
296 if(!l) continue;
297 // world-neutral sounds
298 if(l->cfg == &gamesounds[S_MENUENTER] ||
299 l->cfg == &gamesounds[S_MENUSELECT] ||
300 l->cfg == &gamesounds[S_CALLVOTE] ||
301 l->cfg == &gamesounds[S_VOTEPASS] ||
302 l->cfg == &gamesounds[S_VOTEFAIL]) continue;
303
304 delete_(i--);
305 }
306 };
307
308