1 #include "pch.h"
2 #include "../ogre/common/Def_Str.h"
3 #include "../vdrift/pathmanager.h"
4 #include "SoundBase.h"
5 #include "SoundBaseMgr.h"
6 #include <AL/alc.h>
7 #include <AL/alext.h>
8 #include <AL/efx.h>
9 #include "SoundReverbSets.h"
10 using namespace Ogre;
11
12
13 const float SoundBaseMgr::MAX_DISTANCE = 500.f; // 500
14 const float SoundBaseMgr::REF_DISTANCE = 1.0f; // 1
15 const float SoundBaseMgr::ROLLOFF_FACTOR = 0.05f; // 0.05 0.1
16
17
18 // Init
19 //---------------------------------------------------------------------------------------------
SoundBaseMgr()20 SoundBaseMgr::SoundBaseMgr()
21 :buffers_use(0), buffers_used_max(0), sources_use(0)
22 ,hw_sources_use(0), hw_sources_num(0)
23 ,context(NULL), device(NULL)
24 ,slot(0), effect(0), master_volume(1.f)
25 {
26 hw_sources_map.resize(HW_SRC,0);
27 hw_sources.resize(HW_SRC,0);
28 sources.resize(MAX_BUFFERS,0);
29 buffers.resize(MAX_BUFFERS,0);
30 buffer_file.resize(MAX_BUFFERS);
31 }
32
logList(const char * list)33 void logList(const char *list)
34 {
35 if (!list || *list == '\0')
36 LogO("@@@ None!");
37 else
38 do
39 { LogO(String("@ ") + list);
40 list += strlen(list) + 1;
41 }
42 while (*list != '\0');
43 }
44
Init(std::string snd_device,bool reverb1)45 bool SoundBaseMgr::Init(std::string snd_device, bool reverb1)
46 {
47 reverb = reverb1;
48
49 // list devices
50 LogO("@ ---- Sound devices ----");
51 if (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") != AL_FALSE)
52 logList(alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER));
53 else
54 logList(alcGetString(NULL, ALC_DEVICE_SPECIFIER));
55
56
57 // open device
58 if (snd_device == "")
59 device = alcOpenDevice(NULL);
60 else
61 device = alcOpenDevice(snd_device.c_str());
62
63 if (!device)
64 {
65 LogO("@@@ Sound Init - Could not open device");
66 hasALErrors();
67 return false;
68 }
69
70 // efx
71 ALCboolean efx = alcIsExtensionPresent(device, ALC_EXT_EFX_NAME);
72 if (efx == ALC_FALSE) LogO("@ EFX extention not found !");
73 else if (efx == ALC_TRUE) LogO("@ EFX extension found.");
74
75 ALint attr[4] = { 0 };
76 attr[0] = ALC_MAX_AUXILIARY_SENDS;
77 attr[1] = 4;
78
79
80 // context
81 context = alcCreateContext(device, reverb ? attr : NULL);
82 if (context == NULL ||
83 alcMakeContextCurrent(context) == ALC_FALSE)
84 {
85 LogO("@@@ Sound Init - Could not create context");
86 if (context != NULL)
87 alcDestroyContext(context);
88 alcCloseDevice(device);
89 device = NULL;
90 hasALErrors();
91 return false;
92 }
93
94
95 // log info ----
96 String s,t;
97 LogO("@ @ ---- SoundManager Info ----");
98 s = alGetString(AL_VENDOR); LogO("@ vendor: " + s);
99 s = alGetString(AL_VERSION); LogO("@ version: " + s);
100 s = alGetString(AL_RENDERER); LogO("@ renderer: " + s);
101 //t = alcGetString(device, ALC_DEVICE_SPECIFIER); LogO("@ renderer: " + s + " alc device: " + t);
102 s = alGetString(AL_EXTENSIONS); LogO("@ extensions: " + s);
103 //t = alcGetString(device, ALC_EXTENSIONS); LogO("@ alc extensions: " + s);
104
105
106 // sends
107 ALint iSends = 0;
108 alcGetIntegerv(device, ALC_MAX_AUXILIARY_SENDS, 1, &iSends);
109 LogO("@ Aux Sends per Source: " + toStr(iSends));
110
111
112 // get function pointers
113 alGenEffects = (LPALGENEFFECTS)alGetProcAddress("alGenEffects");
114 alDeleteEffects = (LPALDELETEEFFECTS)alGetProcAddress("alDeleteEffects");
115 alIsEffect = (LPALISEFFECT)alGetProcAddress("alIsEffect");
116 alEffecti = (LPALEFFECTI)alGetProcAddress("alEffecti");
117 alEffectf = (LPALEFFECTF)alGetProcAddress("alEffectf");
118 alEffectfv = (LPALEFFECTFV)alGetProcAddress("alEffectfv");
119 alGenAuxiliaryEffectSlots = (LPALGENAUXILIARYEFFECTSLOTS)alGetProcAddress("alGenAuxiliaryEffectSlots");
120 alAuxiliaryEffectSloti = (LPALAUXILIARYEFFECTSLOTI)alGetProcAddress("alAuxiliaryEffectSloti");
121 alDeleteAuxiliaryEffectSlots = (LPALDELETEAUXILIARYEFFECTSLOTS)alGetProcAddress("alDeleteAuxiliaryEffectSlots");
122
123
124 // doppler
125 alDopplerFactor(0.f); // 1.f todo: vel..
126 //alDopplerVelocity(343.f);
127
128 alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED); //+
129
130 // reverb
131 if (!reverb)
132 {
133 LogO("@ Not using reverb.");
134 return true;
135 }
136
137 // slot is what plays an effect on sources that connect to it
138 alGenAuxiliaryEffectSlots(1, &slot);
139 InitReverMap();
140
141 //SetReverb("MOUNTAINS");
142 return true;
143 }
144
145
146 // Reverb
147 //-----------------------------------------------------------------------------------
SetReverb(std::string name)148 void SoundBaseMgr::SetReverb(std::string name)
149 {
150 if (!device || !reverb) return;
151 sReverb = name;
152 int r = mapReverbs[name] -1;
153 if (r < 0 || r >= RVB_PRESETS_ALL)
154 {
155 r = RVB_GENERIC; // use generic
156 LogO("@ Reverb preset not found! "+name);
157 sReverb = "GENERIC, not found";
158 }
159 const REVERB_PRESET* reverb = &ReverbPresets[r];
160
161 effect = LoadEffect(reverb);
162 if (!effect)
163 LogO("@ Can't load effect !!");
164
165 // Tell the effect slot to use the loaded effect object.
166 // Note that the this copies the effect properties.
167 alAuxiliaryEffectSloti(slot, AL_EFFECTSLOT_EFFECT, effect);
168 if (alGetError() != AL_NO_ERROR)
169 LogO("@ Failed to set effect slot!");
170 alDeleteEffects(1, &effect);
171 }
172
173
174 // Create --
CreateSources()175 void SoundBaseMgr::CreateSources()
176 {
177 if (!device) return;
178 LogO("@ @ Creating hw sources.");
179 int i;
180 for (i = 0; i < HW_SRC; ++i)
181 {
182 alGetError();
183 alGenSources(1, &hw_sources[i]);
184 //alSource3i(source, AL_AUXILIARY_SEND_FILTER, slot, 0, AL_FILTER_NULL);
185
186 if (alGetError() != AL_NO_ERROR) break;
187 alSourcef(hw_sources[i], AL_REFERENCE_DISTANCE, REF_DISTANCE);
188 alSourcef(hw_sources[i], AL_ROLLOFF_FACTOR, ROLLOFF_FACTOR);
189 alSourcef(hw_sources[i], AL_MAX_DISTANCE, MAX_DISTANCE);
190 //LogO(toStr(i)+" +SRC: "+toStr(hw_sources[i]));
191 ++hw_sources_num;
192 }
193
194 for (i = 0; i < HW_SRC; ++i)
195 hw_sources_map[i] = -1;
196
197 buffers_used_max = buffers_use; // save for info
198 buffers_use = 0; //)+ zero after all loaded
199 }
200
201 // Destroy --
DestroySources(bool all)202 void SoundBaseMgr::DestroySources(bool all)
203 {
204 if (!device) return;
205
206 LogO("@ @ Destroying hw sources.");
207 int i;
208 for (int i = 0; i < HW_SRC; ++i)
209 {
210 //LogO(toStr(i)+" -SRC: "+toStr(hw_sources[i]));
211 alSourceStop(hw_sources[i]);
212 alSourcei(hw_sources[i], AL_BUFFER, 0);
213 alDeleteSources(1, &hw_sources[i]);
214 --hw_sources_num;
215 }
216
217 //buffers_use = 0; //)+ needed when loading, zero after CreateSources
218 hw_sources_use = 0;
219 }
220
221 // Destroy
~SoundBaseMgr()222 SoundBaseMgr::~SoundBaseMgr()
223 {
224 if (device)
225 {
226 if (reverb)
227 alDeleteAuxiliaryEffectSlots(1, &slot);
228
229 // sources and buffers
230 DestroySources(true);
231 alDeleteBuffers(MAX_BUFFERS, &buffers[0]);
232 }
233
234 // context and device
235 ALCcontext* context = alcGetCurrentContext();
236 if (context == NULL)
237 { LogO("@ @ SoundManager was disabled.");
238 return;
239 }
240 ALCdevice* device = alcGetContextsDevice(context);
241 alcMakeContextCurrent(NULL);
242 alcDestroyContext(context);
243 if (device)
244 alcCloseDevice(device);
245
246 LogO("@ @ SoundManager destroyed.");
247 }
248
249
250 // Update
251 //-----------------------------------------------------------------------------------
setCamera(Vector3 pos,Vector3 dir,Vector3 up,Vector3 vel)252 void SoundBaseMgr::setCamera(Vector3 pos, Vector3 dir, Vector3 up, Vector3 vel)
253 {
254 if (!device) return;
255 camera_position = pos;
256 recomputeAllSources();
257
258 float o[6];
259 o[0] = dir.x; o[1] = dir.y; o[2] = dir.z;
260 o[3] = up.x; o[4] = up.y; o[5] = up.z;
261
262 alListener3f(AL_POSITION, pos.x, pos.y, pos.z);
263 alListener3f(AL_VELOCITY, vel.x, vel.y, vel.z);
264 alListenerfv(AL_ORIENTATION, o);
265 }
266
compareByAudibility(std::pair<int,float> a,std::pair<int,float> b)267 bool compareByAudibility(std::pair<int, float> a, std::pair<int, float> b)
268 {
269 return a.second > b.second;
270 }
271
272 // called when camera moves
recomputeAllSources()273 void SoundBaseMgr::recomputeAllSources()
274 {
275 if (!device) return;
276
277 int i;
278 for (i=0; i < sources_use; i++)
279 {
280 sources[i]->computeAudibility(camera_position);
281 src_audible[i].first = i;
282 src_audible[i].second = sources[i]->audibility;
283 }
284
285 // sort first 'num_hardware_sources' sources by audibility
286 // see: https://en.wikipedia.org/wiki/Selection_algorithm
287 if (sources_use - 1 > hw_sources_num)
288 std::nth_element(src_audible, src_audible+hw_sources_num, src_audible+sources_use-1, compareByAudibility);
289
290 // retire out of range sources first
291 for (i=0; i < sources_use; i++)
292 if (sources[src_audible[i].first]->hw_id != -1 && (i >= hw_sources_num || src_audible[i].second == 0))
293 retire(src_audible[i].first);
294
295 // assign new sources
296 for (i=0; i < std::min(sources_use, hw_sources_num); i++)
297 if (sources[src_audible[i].first]->hw_id == -1 && src_audible[i].second > 0)
298 for (int j=0; j < hw_sources_num; j++)
299 if (hw_sources_map[j] == -1)
300 {
301 assign(src_audible[i].first, j);
302 break;
303 }
304 }
305
306
307 // recompute Source
308 //---------------------------------------------------------------------------------------------------------------------------
recomputeSource(int id,int reason,float fl,Vector3 * vec)309 void SoundBaseMgr::recomputeSource(int id, int reason, float fl, Vector3* vec)
310 {
311 if (!device) return;
312 sources[id]->computeAudibility(camera_position);
313
314 if (sources[id]->audibility == 0.0f)
315 {
316 if (sources[id]->hw_id != -1)
317 // retire the source if it is currently assigned
318 retire(id);
319 }else
320 {
321 // this is a potentially audible sources[id]
322 if (sources[id]->hw_id != -1)
323 {
324 // sources[id] already playing
325 // update the AL settings
326 switch (reason)
327 {
328 case REASON_GAIN: alSourcef(hw_sources[sources[id]->hw_id], AL_GAIN, fl * master_volume); break;
329 case REASON_PTCH: alSourcef(hw_sources[sources[id]->hw_id], AL_PITCH, fl); break;
330 case REASON_POS: if (!sources[id]->is2D)
331 alSource3f(hw_sources[sources[id]->hw_id], AL_POSITION, vec->x, vec->y, vec->z); break;
332 case REASON_VEL: alSource3f(hw_sources[sources[id]->hw_id], AL_VELOCITY, vec->x, vec->y, vec->z); break;
333
334 case REASON_PLAY: alSourcePlay(hw_sources[sources[id]->hw_id]); break;
335 case REASON_STOP: alSourceStop(hw_sources[sources[id]->hw_id]); break;
336 case REASON_LOOP: alSourcei(hw_sources[sources[id]->hw_id], AL_LOOPING, fl > 0.5f ? AL_TRUE : AL_FALSE); break;
337 case REASON_SEEK: alSourcei(hw_sources[sources[id]->hw_id], AL_SAMPLE_OFFSET, fl); break;
338 default: break;
339 }
340 }else
341 {
342 // try to make it play by the hardware
343 // check if there is one free sources[id] in the pool
344 if (hw_sources_use < hw_sources_num)
345 {
346 for (int i=0; i < hw_sources_num; i++)
347 {
348 if (hw_sources_map[i] == -1)
349 {
350 assign(id, i);
351 break;
352 }
353 }
354 }else
355 {
356 // now, compute who is the faintest
357 // note: we know the table m_hardware_sources_map is full!
358 float fv = 1.0f;
359 int al_faintest = 0;
360 for (int i=0; i < hw_sources_num; i++)
361 {
362 if (hw_sources_map[i] >= 0 && sources[hw_sources_map[i]]->audibility < fv)
363 {
364 fv = sources[hw_sources_map[i]]->audibility;
365 al_faintest = i;
366 }
367 }
368 // check to ensure that the sound is louder than the faintest sound currently playing
369 if (fv < sources[id]->audibility)
370 {
371 // this new sources[id] is louder than the faintest!
372 retire(hw_sources_map[al_faintest]);
373 assign(id, al_faintest);
374 }
375 // else this sources[id] is too faint, we don't play it!
376 }
377 }
378 }
379 }
380
381
382 // assign
383 //-----------------------------------------------------------------------------------
384
assign(int id,int hw_id)385 void SoundBaseMgr::assign(int id, int hw_id)
386 {
387 if (!device) return;
388 sources[id]->hw_id = hw_id;
389 hw_sources_map[hw_id] = id;
390
391 // the hardware source is supposed to be stopped!
392 ALuint source = hw_sources[hw_id];
393 alSourcei(source, AL_BUFFER, sources[id]->buffer);
394
395 // use reverb +
396 if (reverb)
397 alSource3i(source, AL_AUXILIARY_SEND_FILTER,
398 !sources[id]->is2D ? slot : AL_EFFECTSLOT_NULL, 0, AL_FILTER_NULL);
399
400 alSourcef(source, AL_GAIN, sources[id]->gain * master_volume);
401 alSourcei(source, AL_LOOPING, sources[id]->loop ? AL_TRUE : AL_FALSE);
402 alSourcef(source, AL_PITCH, sources[id]->pitch);
403
404 alSource3f(source, AL_POSITION, sources[id]->pos.x, sources[id]->pos.y, sources[id]->pos.z);
405 alSource3f(source, AL_VELOCITY, sources[id]->vel.x, sources[id]->vel.y, sources[id]->vel.z);
406
407 // hud, 2d
408 if (sources[id]->is2D)
409 {
410 alSourcei(source, AL_SOURCE_RELATIVE, AL_TRUE);
411 alSource3f(source, AL_POSITION, 0.f,0.f,0.f);
412 //alSourcef(source, AL_ROLLOFF_FACTOR, 0.f);
413 }
414
415 if (sources[id]->should_play)
416 alSourcePlay(hw_sources[hw_id]);
417
418 ++hw_sources_use;
419 }
420
421
retire(int id)422 void SoundBaseMgr::retire(int id)
423 {
424 if (!device) return;
425 //if (sources[id]->is2D) return;
426 if (sources[id]->hw_id == -1) return;
427
428 alSourceStop(hw_sources[sources[id]->hw_id]);
429 hw_sources_map[sources[id]->hw_id] = -1;
430 sources[id]->hw_id = -1;
431 --hw_sources_use;
432 }
433
434
435 // utility
pauseAll(bool mute)436 void SoundBaseMgr::pauseAll(bool mute)
437 {
438 if (!device) return;
439 alListenerf(AL_GAIN, mute ? 0.0f : master_volume);
440 }
441
setMasterVolume(float vol)442 void SoundBaseMgr::setMasterVolume(float vol)
443 {
444 if (!device) return;
445 master_volume = vol;
446 alListenerf(AL_GAIN, master_volume);
447 }
448