RigsofRods
Soft-body Physics Simulation
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
SoundManager.cpp
Go to the documentation of this file.
1 /*
2  This source file is part of Rigs of Rods
3  Copyright 2005-2012 Pierre-Michel Ricordel
4  Copyright 2007-2012 Thomas Fischer
5  Copyright 2013-2020 Petr Ohlidal
6 
7  For more information, see http://www.rigsofrods.org/
8 
9  Rigs of Rods is free software: you can redistribute it and/or modify
10  it under the terms of the GNU General Public License version 3, as
11  published by the Free Software Foundation.
12 
13  Rigs of Rods is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  GNU General Public License for more details.
17 
18  You should have received a copy of the GNU General Public License
19  along with Rigs of Rods. If not, see <http://www.gnu.org/licenses/>.
20 */
21 
22 #ifdef USE_OPENAL
23 
24 #include "SoundManager.h"
25 
26 #include "AeroEngine.h"
27 #include "Application.h"
28 #include "IWater.h"
29 #include "ScrewProp.h"
30 #include "Sound.h"
31 
32 #include <OgreResourceGroupManager.h>
33 
34 #define LOGSTREAM Ogre::LogManager::getSingleton().stream() << "[RoR|Audio] "
35 
36 bool _checkALErrors(const char* filename, int linenum)
37 {
38  int err = alGetError();
39  if (err != AL_NO_ERROR)
40  {
41  char buf[1000] = {};
42  snprintf(buf, 1000, "OpenAL Error: %s (0x%x), @ %s:%d", alGetString(err), err, filename, linenum);
43  LOGSTREAM << buf;
44  return true;
45  }
46  return false;
47 }
48 
49 #define hasALErrors() _checkALErrors(__FILE__, __LINE__)
50 
51 using namespace RoR;
52 using namespace Ogre;
53 
54 const float SoundManager::MAX_DISTANCE = 500.0f;
55 const float SoundManager::ROLLOFF_FACTOR = 1.0f;
56 const float SoundManager::REFERENCE_DISTANCE = 7.5f;
57 
59 {
60  if (App::audio_device_name->getStr() == "")
61  {
62  LOGSTREAM << "No audio device configured, opening default.";
63  audio_device = alcOpenDevice(nullptr);
64  }
65  else
66  {
67  audio_device = alcOpenDevice(App::audio_device_name->getStr().c_str());
68  if (!audio_device)
69  {
70  LOGSTREAM << "Failed to open configured audio device \"" << App::audio_device_name->getStr() << "\", opening default.";
72  audio_device = alcOpenDevice(nullptr);
73  }
74  }
75 
76  if (!audio_device)
77  {
78  LOGSTREAM << "Failed to open default audio device. Sound disabled.";
79  hasALErrors();
80  return;
81  }
82 
83  sound_context = alcCreateContext(audio_device, NULL);
84 
85  if (!sound_context)
86  {
87  alcCloseDevice(audio_device);
88  audio_device = NULL;
89  hasALErrors();
90  return;
91  }
92 
93  alcMakeContextCurrent(sound_context);
94 
95  if (alGetString(AL_VENDOR)) LOG("SoundManager: OpenAL vendor is: " + String(alGetString(AL_VENDOR)));
96  if (alGetString(AL_VERSION)) LOG("SoundManager: OpenAL version is: " + String(alGetString(AL_VERSION)));
97  if (alGetString(AL_RENDERER)) LOG("SoundManager: OpenAL renderer is: " + String(alGetString(AL_RENDERER)));
98  if (alGetString(AL_EXTENSIONS)) LOG("SoundManager: OpenAL extensions are: " + String(alGetString(AL_EXTENSIONS)));
99  if (alcGetString(audio_device, ALC_DEVICE_SPECIFIER)) LOG("SoundManager: OpenAL device is: " + String(alcGetString(audio_device, ALC_DEVICE_SPECIFIER)));
100  if (alcGetString(audio_device, ALC_EXTENSIONS)) LOG("SoundManager: OpenAL ALC extensions are: " + String(alcGetString(audio_device, ALC_EXTENSIONS)));
101 
102  // initialize use of OpenAL EFX extensions
103  m_efx_is_available = alcIsExtensionPresent(audio_device, "ALC_EXT_EFX");
104  if (m_efx_is_available)
105  {
106  LOG("SoundManager: Found OpenAL EFX extension");
107 
108  // Get OpenAL function pointers
109  alGenEffects = (LPALGENEFFECTS)alGetProcAddress("alGenEffects");
110  alDeleteEffects = (LPALDELETEEFFECTS)alGetProcAddress("alDeleteEffects");
111  alIsEffect = (LPALISEFFECT)alGetProcAddress("alIsEffect");
112  alEffecti = (LPALEFFECTI)alGetProcAddress("alEffecti");
113  alEffectf = (LPALEFFECTF)alGetProcAddress("alEffectf");
114  alEffectfv = (LPALEFFECTFV)alGetProcAddress("alEffectfv");
115  alGenFilters = (LPALGENFILTERS)alGetProcAddress("alGenFilters");
116  alDeleteFilters = (LPALDELETEFILTERS)alGetProcAddress("alDeleteFilters");
117  alIsFilter = (LPALISFILTER)alGetProcAddress("alIsFilter");
118  alFilteri = (LPALFILTERI)alGetProcAddress("alFilteri");
119  alFilterf = (LPALFILTERF)alGetProcAddress("alFilterf");
120  alGenAuxiliaryEffectSlots = (LPALGENAUXILIARYEFFECTSLOTS)alGetProcAddress("alGenAuxiliaryEffectSlots");
121  alDeleteAuxiliaryEffectSlots = (LPALDELETEAUXILIARYEFFECTSLOTS)alGetProcAddress("alDeleteAuxiliaryEffectSlots");
122  alIsAuxiliaryEffectSlot = (LPALISAUXILIARYEFFECTSLOT)alGetProcAddress("alIsAuxiliaryEffectSlot");
123  alAuxiliaryEffectSloti = (LPALAUXILIARYEFFECTSLOTI)alGetProcAddress("alAuxiliaryEffectSloti");
124  alAuxiliaryEffectSlotf = (LPALAUXILIARYEFFECTSLOTF)alGetProcAddress("alAuxiliaryEffectSlotf");
125  alAuxiliaryEffectSlotfv = (LPALAUXILIARYEFFECTSLOTFV)alGetProcAddress("alAuxiliaryEffectSlotfv");
126 
127  if (App::audio_enable_efx->getBool())
128  {
129  // allow user to change reverb engines at will
130  switch (App::audio_efx_reverb_engine->getEnum<EfxReverbEngine>())
131  {
132  case EfxReverbEngine::EAXREVERB: m_efx_reverb_engine = EfxReverbEngine::EAXREVERB; break;
133  case EfxReverbEngine::REVERB: m_efx_reverb_engine = EfxReverbEngine::REVERB; break;
134  default:
135  m_efx_reverb_engine = EfxReverbEngine::NONE;
136  LOG("SoundManager: Reverb engine disabled");
137  }
138 
139  if (m_efx_reverb_engine == EfxReverbEngine::EAXREVERB)
140  {
141  if (alGetEnumValue("AL_EFFECT_EAXREVERB") != 0)
142  {
143  LOG("SoundManager: OpenAL driver supports AL_EFFECT_EAXREVERB, using it");
144  }
145  else
146  {
147  LOG("SoundManager: AL_EFFECT_EAXREVERB requested but OpenAL driver does not support it, falling back to standard reverb. Advanced features, such as reflection panning, will not be available");
148  m_efx_reverb_engine = EfxReverbEngine::REVERB;
149  }
150  }
151  else if (m_efx_reverb_engine == EfxReverbEngine::REVERB)
152  {
153  LOG("SoundManager: Using OpenAL standard reverb");
154  }
155 
156  // create effect slot for the listener
157  if (!this->alIsAuxiliaryEffectSlot(m_listener_slot))
158  {
159  alGetError();
160 
161  this->alGenAuxiliaryEffectSlots(1, &m_listener_slot);
162  ALuint error = alGetError();
163 
164  if (error != AL_NO_ERROR)
165  {
166  LOG("SoundManager: alGenAuxiliaryEffectSlots for listener_slot failed: " + TOSTRING(alGetString(error)));
167  m_listener_slot = AL_EFFECTSLOT_NULL;
168  }
169  }
170 
171  this->PrepopulateEfxPropertiesMap();
172 
173  /*
174  Create filter for obstruction
175  Currently we don't check for how much high-frequency content the obstacle
176  lets through. We assume it's a hard surface with significant absorption
177  of high frequencies (which should be true for trucks, buildings and terrain).
178  */
179  alGetError();
180 
181  this->alGenFilters(1, &m_efx_outdoor_obstruction_lowpass_filter_id);
182  ALuint e = alGetError();
183 
184  if (e != AL_NO_ERROR)
185  {
186  m_efx_outdoor_obstruction_lowpass_filter_id = AL_FILTER_NULL;
187  }
188  else
189  {
190  this->alFilteri(m_efx_outdoor_obstruction_lowpass_filter_id, AL_FILTER_TYPE, AL_FILTER_LOWPASS);
191  this->alFilterf(m_efx_outdoor_obstruction_lowpass_filter_id, AL_LOWPASS_GAIN, 0.33f);
192  this->alFilterf(m_efx_outdoor_obstruction_lowpass_filter_id, AL_LOWPASS_GAINHF, 0.25f);
193  }
194 
195  /*
196  Create wet path filter for occlusion
197  Currently we don't check for how much high-frequency content the encompassing walls
198  let through. We assume it's a hard surface with significant absorption
199  of high frequencies (which should be true for most types of buildings).
200  */
201  alGetError();
202 
203  this->alGenFilters(1, &m_efx_occlusion_wet_path_lowpass_filter_id);
204  e = alGetError();
205 
206  if (e != AL_NO_ERROR)
207  {
208  m_efx_occlusion_wet_path_lowpass_filter_id = AL_FILTER_NULL;
209  }
210  else
211  {
212  this->alFilteri(m_efx_occlusion_wet_path_lowpass_filter_id, AL_FILTER_TYPE, AL_FILTER_LOWPASS);
213  this->alFilterf(m_efx_occlusion_wet_path_lowpass_filter_id, AL_LOWPASS_GAIN, 0.33f);
214  this->alFilterf(m_efx_occlusion_wet_path_lowpass_filter_id, AL_LOWPASS_GAINHF, 0.25f);
215  }
216  }
217  }
218  else
219  {
220  LOG("SoundManager: OpenAL EFX extension not found, disabling EFX");
222  }
223 
224  // generate the AL sources
225  for (hardware_sources_num = 0; hardware_sources_num < MAX_HARDWARE_SOURCES; hardware_sources_num++)
226  {
227  alGetError();
228  alGenSources(1, &hardware_sources[hardware_sources_num]);
229  if (alGetError() != AL_NO_ERROR)
230  break;
231  alSourcef(hardware_sources[hardware_sources_num], AL_REFERENCE_DISTANCE, REFERENCE_DISTANCE);
232  alSourcef(hardware_sources[hardware_sources_num], AL_ROLLOFF_FACTOR, ROLLOFF_FACTOR);
233  alSourcef(hardware_sources[hardware_sources_num], AL_MAX_DISTANCE, MAX_DISTANCE);
234 
235  // connect source to listener slot effect
236  if (App::audio_enable_efx->getBool())
237  {
238  alSource3i(hardware_sources[hardware_sources_num], AL_AUXILIARY_SEND_FILTER, m_listener_slot, 0, AL_FILTER_NULL);
239  }
240  }
241 
242  alDopplerFactor(App::audio_doppler_factor->getFloat());
243  alSpeedOfSound(343.3f);
244 
245  for (int i = 0; i < MAX_HARDWARE_SOURCES; i++)
246  {
247  hardware_sources_map[i] = -1;
248  }
249 
250 
251 }
252 
254 {
255  // delete the sources and buffers
256  alDeleteSources(MAX_HARDWARE_SOURCES, hardware_sources);
257  alDeleteBuffers(MAX_AUDIO_BUFFERS, audio_buffers);
258 
259  if (m_efx_is_available)
260  {
261  if (this->alIsFilter(m_efx_outdoor_obstruction_lowpass_filter_id))
262  {
263  this->alDeleteFilters(1, &m_efx_outdoor_obstruction_lowpass_filter_id);
264  }
265 
266  if (this->alIsFilter(m_efx_occlusion_wet_path_lowpass_filter_id))
267  {
268  this->alDeleteFilters(1, &m_efx_occlusion_wet_path_lowpass_filter_id);
269  }
270 
271  if (this->alIsAuxiliaryEffectSlot(m_listener_slot))
272  {
273  this->alAuxiliaryEffectSloti(m_listener_slot, AL_EFFECTSLOT_EFFECT, AL_EFFECTSLOT_NULL);
274  this->alDeleteAuxiliaryEffectSlots(1, &m_listener_slot);
275  m_listener_slot = 0;
276  }
277  }
278 
279  CleanUp();
280 
281  // destroy the sound context and device
282  sound_context = alcGetCurrentContext();
283  audio_device = alcGetContextsDevice(sound_context);
284  alcMakeContextCurrent(NULL);
285  alcDestroyContext(sound_context);
286  if (audio_device)
287  {
288  alcCloseDevice(audio_device);
289  }
290  LOG("SoundManager destroyed.");
291 }
292 
294 {
295  if (m_efx_is_available)
296  {
297  m_listener_efx_reverb_properties = nullptr;
298  if (this->alIsAuxiliaryEffectSlot(m_listener_slot))
299  {
300  this->alAuxiliaryEffectSloti(m_listener_slot, AL_EFFECTSLOT_EFFECT, AL_EFFECTSLOT_NULL);
301  }
302 
303  for (auto it = m_efx_effect_id_map.begin(); it != m_efx_effect_id_map.end();)
304  {
305  this->DeleteAlEffect(it->second);
306  it = m_efx_effect_id_map.erase(it);
307  }
308  }
309 
310  // TODO: Delete Sounds and buffers
311 }
312 
313 const EFXEAXREVERBPROPERTIES* SoundManager::GetEfxProperties(const std::string& efx_preset_name) const
314 {
315  const auto it = m_efx_properties_map.find(efx_preset_name);
316 
317  if (it != m_efx_properties_map.end())
318  {
319  return &it->second;
320  }
321  else
322  {
323  return nullptr;
324  }
325 }
326 
328 {
329  m_efx_properties_map["EFX_REVERB_PRESET_GENERIC"] = EFX_REVERB_PRESET_GENERIC;
330  m_efx_properties_map["EFX_REVERB_PRESET_ROOM"] = EFX_REVERB_PRESET_ROOM;
331  m_efx_properties_map["EFX_REVERB_PRESET_CAVE"] = EFX_REVERB_PRESET_CAVE;
332  m_efx_properties_map["EFX_REVERB_PRESET_ARENA"] = EFX_REVERB_PRESET_ARENA;
333  m_efx_properties_map["EFX_REVERB_PRESET_HANGAR"] = EFX_REVERB_PRESET_HANGAR;
334  m_efx_properties_map["EFX_REVERB_PRESET_ALLEY"] = EFX_REVERB_PRESET_ALLEY;
335  m_efx_properties_map["EFX_REVERB_PRESET_HALLWAY"] = EFX_REVERB_PRESET_HALLWAY;
336  m_efx_properties_map["EFX_REVERB_PRESET_FOREST"] = EFX_REVERB_PRESET_FOREST;
337  m_efx_properties_map["EFX_REVERB_PRESET_CITY"] = EFX_REVERB_PRESET_CITY;
338  m_efx_properties_map["EFX_REVERB_PRESET_MOUNTAINS"] = EFX_REVERB_PRESET_MOUNTAINS;
339  m_efx_properties_map["EFX_REVERB_PRESET_QUARRY"] = EFX_REVERB_PRESET_QUARRY;
340  m_efx_properties_map["EFX_REVERB_PRESET_PLAIN"] = EFX_REVERB_PRESET_PLAIN;
341  m_efx_properties_map["EFX_REVERB_PRESET_PARKINGLOT"] = EFX_REVERB_PRESET_PARKINGLOT;
342  m_efx_properties_map["EFX_REVERB_PRESET_UNDERWATER"] = EFX_REVERB_PRESET_UNDERWATER;
343  m_efx_properties_map["EFX_REVERB_PRESET_DRUGGED"] = EFX_REVERB_PRESET_DRUGGED;
344  m_efx_properties_map["EFX_REVERB_PRESET_DIZZY"] = EFX_REVERB_PRESET_DIZZY;
345  m_efx_properties_map["EFX_REVERB_PRESET_CASTLE_SHORTPASSAGE"] = EFX_REVERB_PRESET_CASTLE_SHORTPASSAGE;
346  m_efx_properties_map["EFX_REVERB_PRESET_CASTLE_LARGEROOM"] = EFX_REVERB_PRESET_CASTLE_LARGEROOM;
347  m_efx_properties_map["EFX_REVERB_PRESET_CASTLE_LONGPASSAGE"] = EFX_REVERB_PRESET_CASTLE_LONGPASSAGE;
348  m_efx_properties_map["EFX_REVERB_PRESET_CASTLE_HALL"] = EFX_REVERB_PRESET_CASTLE_HALL;
349  m_efx_properties_map["EFX_REVERB_PRESET_CASTLE_COURTYARD"] = EFX_REVERB_PRESET_CASTLE_COURTYARD;
350  m_efx_properties_map["EFX_REVERB_PRESET_FACTORY_SMALLROOM"] = EFX_REVERB_PRESET_FACTORY_SMALLROOM;
351  m_efx_properties_map["EFX_REVERB_PRESET_FACTORY_SHORTPASSAGE"] = EFX_REVERB_PRESET_FACTORY_SHORTPASSAGE;
352  m_efx_properties_map["EFX_REVERB_PRESET_FACTORY_MEDIUMROOM"] = EFX_REVERB_PRESET_FACTORY_MEDIUMROOM;
353  m_efx_properties_map["EFX_REVERB_PRESET_FACTORY_LARGEROOM"] = EFX_REVERB_PRESET_FACTORY_LARGEROOM;
354  m_efx_properties_map["EFX_REVERB_PRESET_FACTORY_LONGPASSAGE"] = EFX_REVERB_PRESET_FACTORY_LONGPASSAGE;
355  m_efx_properties_map["EFX_REVERB_PRESET_FACTORY_HALL"] = EFX_REVERB_PRESET_FACTORY_HALL;
356  m_efx_properties_map["EFX_REVERB_PRESET_FACTORY_COURTYARD"] = EFX_REVERB_PRESET_FACTORY_COURTYARD;
357  m_efx_properties_map["EFX_REVERB_PRESET_FACTORY_ALCOVE"] = EFX_REVERB_PRESET_FACTORY_ALCOVE;
358  m_efx_properties_map["EFX_REVERB_PRESET_SPACESTATION_SMALLROOM"] = EFX_REVERB_PRESET_SPACESTATION_SMALLROOM;
359  m_efx_properties_map["EFX_REVERB_PRESET_SPACESTATION_SHORTPASSAGE"] = EFX_REVERB_PRESET_SPACESTATION_SHORTPASSAGE;
360  m_efx_properties_map["EFX_REVERB_PRESET_SPACESTATION_MEDIUMROOM"] = EFX_REVERB_PRESET_SPACESTATION_MEDIUMROOM;
361  m_efx_properties_map["EFX_REVERB_PRESET_SPACESTATION_LARGEROOM"] = EFX_REVERB_PRESET_SPACESTATION_LARGEROOM;
362  m_efx_properties_map["EFX_REVERB_PRESET_SPACESTATION_LONGPASSAGE"] = EFX_REVERB_PRESET_SPACESTATION_LONGPASSAGE;
363  m_efx_properties_map["EFX_REVERB_PRESET_SPACESTATION_HALL"] = EFX_REVERB_PRESET_SPACESTATION_HALL;
364  m_efx_properties_map["EFX_REVERB_PRESET_WOODEN_SMALLROOM"] = EFX_REVERB_PRESET_WOODEN_SMALLROOM;
365  m_efx_properties_map["EFX_REVERB_PRESET_WOODEN_SHORTPASSAGE"] = EFX_REVERB_PRESET_WOODEN_SHORTPASSAGE;
366  m_efx_properties_map["EFX_REVERB_PRESET_WOODEN_MEDIUMROOM"] = EFX_REVERB_PRESET_WOODEN_MEDIUMROOM;
367  m_efx_properties_map["EFX_REVERB_PRESET_WOODEN_LARGEROOM"] = EFX_REVERB_PRESET_WOODEN_LARGEROOM;
368  m_efx_properties_map["EFX_REVERB_PRESET_WOODEN_LONGPASSAGE"] = EFX_REVERB_PRESET_WOODEN_LONGPASSAGE;
369  m_efx_properties_map["EFX_REVERB_PRESET_WOODEN_HALL"] = EFX_REVERB_PRESET_WOODEN_HALL;
370  m_efx_properties_map["EFX_REVERB_PRESET_WOODEN_COURTYARD"] = EFX_REVERB_PRESET_WOODEN_COURTYARD;
371  m_efx_properties_map["EFX_REVERB_PRESET_WOODEN_ALCOVE"] = EFX_REVERB_PRESET_WOODEN_ALCOVE;
372  m_efx_properties_map["EFX_REVERB_PRESET_SPORT_EMPTYSTADIUM"] = EFX_REVERB_PRESET_SPORT_EMPTYSTADIUM;
373  m_efx_properties_map["EFX_REVERB_PRESET_SPORT_FULLSTADIUM"] = EFX_REVERB_PRESET_SPORT_FULLSTADIUM;
374  m_efx_properties_map["EFX_REVERB_PRESET_SPORT_STADIUMTANNOY"] = EFX_REVERB_PRESET_SPORT_STADIUMTANNOY;
375  m_efx_properties_map["EFX_REVERB_PRESET_PREFAB_WORKSHOP"] = EFX_REVERB_PRESET_PREFAB_WORKSHOP;
376  m_efx_properties_map["EFX_REVERB_PRESET_PREFAB_OUTHOUSE"] = EFX_REVERB_PRESET_PREFAB_OUTHOUSE;
377  m_efx_properties_map["EFX_REVERB_PRESET_PREFAB_CARAVAN"] = EFX_REVERB_PRESET_PREFAB_CARAVAN;
378  m_efx_properties_map["EFX_REVERB_PRESET_PIPE_LARGE"] = EFX_REVERB_PRESET_PIPE_LARGE;
379  m_efx_properties_map["EFX_REVERB_PRESET_PIPE_LONGTHIN"] = EFX_REVERB_PRESET_PIPE_LONGTHIN;
380  m_efx_properties_map["EFX_REVERB_PRESET_PIPE_RESONANT"] = EFX_REVERB_PRESET_PIPE_RESONANT;
381  m_efx_properties_map["EFX_REVERB_PRESET_OUTDOORS_BACKYARD"] = EFX_REVERB_PRESET_OUTDOORS_BACKYARD;
382  m_efx_properties_map["EFX_REVERB_PRESET_OUTDOORS_ROLLINGPLAINS"] = EFX_REVERB_PRESET_OUTDOORS_ROLLINGPLAINS;
383  m_efx_properties_map["EFX_REVERB_PRESET_OUTDOORS_DEEPCANYON"] = EFX_REVERB_PRESET_OUTDOORS_DEEPCANYON;
384  m_efx_properties_map["EFX_REVERB_PRESET_OUTDOORS_CREEK"] = EFX_REVERB_PRESET_OUTDOORS_CREEK;
385  m_efx_properties_map["EFX_REVERB_PRESET_OUTDOORS_VALLEY"] = EFX_REVERB_PRESET_OUTDOORS_VALLEY;
386  m_efx_properties_map["EFX_REVERB_PRESET_MOOD_HEAVEN"] = EFX_REVERB_PRESET_MOOD_HEAVEN;
387  m_efx_properties_map["EFX_REVERB_PRESET_MOOD_HELL"] = EFX_REVERB_PRESET_MOOD_HELL;
388  m_efx_properties_map["EFX_REVERB_PRESET_MOOD_MEMORY"] = EFX_REVERB_PRESET_MOOD_MEMORY;
389  m_efx_properties_map["EFX_REVERB_PRESET_DRIVING_COMMENTATOR"] = EFX_REVERB_PRESET_DRIVING_COMMENTATOR;
390  m_efx_properties_map["EFX_REVERB_PRESET_DRIVING_PITGARAGE"] = EFX_REVERB_PRESET_DRIVING_PITGARAGE;
391  m_efx_properties_map["EFX_REVERB_PRESET_DRIVING_INCAR_RACER"] = EFX_REVERB_PRESET_DRIVING_INCAR_RACER;
392  m_efx_properties_map["EFX_REVERB_PRESET_DRIVING_INCAR_SPORTS"] = EFX_REVERB_PRESET_DRIVING_INCAR_SPORTS;
393  m_efx_properties_map["EFX_REVERB_PRESET_DRIVING_INCAR_LUXURY"] = EFX_REVERB_PRESET_DRIVING_INCAR_LUXURY;
394  m_efx_properties_map["EFX_REVERB_PRESET_DRIVING_FULLGRANDSTAND"] = EFX_REVERB_PRESET_DRIVING_FULLGRANDSTAND;
395  m_efx_properties_map["EFX_REVERB_PRESET_DRIVING_EMPTYGRANDSTAND"] = EFX_REVERB_PRESET_DRIVING_EMPTYGRANDSTAND;
396  m_efx_properties_map["EFX_REVERB_PRESET_DRIVING_TUNNEL"] = EFX_REVERB_PRESET_DRIVING_TUNNEL;
397  m_efx_properties_map["EFX_REVERB_PRESET_CITY_STREETS"] = EFX_REVERB_PRESET_CITY_STREETS;
398  m_efx_properties_map["EFX_REVERB_PRESET_CITY_SUBWAY"] = EFX_REVERB_PRESET_CITY_SUBWAY;
399  m_efx_properties_map["EFX_REVERB_PRESET_CITY_UNDERPASS"] = EFX_REVERB_PRESET_CITY_UNDERPASS;
400  m_efx_properties_map["EFX_REVERB_PRESET_CITY_ABANDONED"] = EFX_REVERB_PRESET_CITY_ABANDONED;
401 }
402 
403 void SoundManager::Update(const float dt)
404 {
405  if (!audio_device)
406  return;
407 
408  const auto water = App::GetGameContext()->GetTerrain()->getWater();
409  m_listener_is_underwater = (water != nullptr ? water->IsUnderWater(m_listener_position) : false);
410 
411  this->UpdateGlobalDopplerFactor();
412  this->recomputeAllSources();
413  this->UpdateAlListener();
414  this->UpdateListenerEnvironment();
415  this->UpdateEfxSpecificProperties(dt);
416 }
417 
419 {
420  if (App::GetGameContext()->GetActorManager()->IsSimulationPaused() && App::audio_sim_pause_disables_doppler_effect->getBool())
421  {
422  /*
423  If the simulation is paused, the listener's velocity suddenly becomes close to zero, which could be a large
424  difference to before if the listener was following a fast vehicle. Additionally, users might be confused when
425  they rotate around objects moving at least as fast as Mach 1 since the Doppler shift changes significantly and
426  there would be no sound in front of the object.
427  Hence, we provide an option for disabling the Doppler effect while the simulation is paused.
428  */
429  this->SetDopplerFactor(0);
430  }
431  else {
432  this->SetDopplerFactor(App::audio_doppler_factor->getFloat());
433  }
434 }
435 
437 {
438  if (!App::audio_enable_efx->getBool()) { return; }
439 
440  for (int hardware_index = 0; hardware_index < hardware_sources_num; ++hardware_index)
441  {
442  this->UpdateSourceSpecificDopplerFactor(hardware_index);
443 
444  // update air absorption factor
445  alSourcef(hardware_sources[hardware_index], AL_AIR_ABSORPTION_FACTOR, m_air_absorption_factor);
446 
447  this->UpdateSourceFilters(hardware_index);
448  }
449 
450  this->UpdateListenerEffectSlot(dt);
451 
452  if (App::audio_enable_directed_sounds->getBool())
453  {
454  this->UpdateDirectedSounds();
455  }
456 }
457 
458 void SoundManager::UpdateSourceSpecificDopplerFactor(const int hardware_index) const
459 {
460  const SoundPtr& corresponding_sound = audio_sources[hardware_sources_map[hardware_index]];
462 
463  // identify actor to which the Sound instance corresponding to the hardware source belongs
464  for (const ActorPtr& actor : actors)
465  {
466  for (int soundsource_index = 0; soundsource_index < actor->ar_num_soundsources; ++soundsource_index)
467  {
468  const soundsource_t& soundsource = actor->ar_soundsources[soundsource_index];
469  const int num_sounds_of_ss = soundsource.ssi->getTemplate()->getNumSounds();
470 
471  for (int num_sound = 0; num_sound < num_sounds_of_ss; ++num_sound)
472  {
473  // update the Doppler factor if the Sound belongs to the actor
474  if (soundsource.ssi->getSound(num_sound) == corresponding_sound)
475  {
477  || !actor->ar_physics_paused)
478  {
479  this->SetDopplerFactor(hardware_sources[hardware_index], 1.0f);
480  }
481  else {
482  this->SetDopplerFactor(hardware_sources[hardware_index], 0.0f);
483  }
484 
485  return;
486  }
487  }
488  }
489  }
490 }
491 
492 void SoundManager::SetListener(Ogre::Vector3 position, Ogre::Vector3 direction, Ogre::Vector3 up, Ogre::Vector3 velocity)
493 {
494  m_listener_position = position;
495  m_listener_direction = direction;
496  m_listener_up = up;
497  m_listener_velocity = velocity;
498 }
499 
500 void SoundManager::SetDopplerFactor(const ALuint hardware_source, const float doppler_factor) const
501 {
502  App::audio_enable_efx->getBool() ? alSourcef(hardware_source, AL_DOPPLER_FACTOR, doppler_factor) : void();
503 }
504 
506 {
507  const EFXEAXREVERBPROPERTIES* listener_reverb_properties = nullptr;
508 
510  {
511  if (this->ListenerIsUnderwater())
512  {
513  this->SetSpeedOfSound(1522.0f); // assume listener is in sea water (i.e. salt water)
514  /*
515  * According to the Francois-Garrison formula for frequency-dependant absorption at 5kHz in seawater,
516  * the absorption should be 0.334 dB/km. OpenAL multiplies the Air Absorption Factor with an internal
517  * value of 0.05dB/m, so we need a factor of 0.00668f.
518  */
519  this->SetAirAbsorptionFactor(0.00668f);
520  }
521  else
522  {
523  this->SetSpeedOfSound(343.3f); // assume listener is in air at 20° celsius
524  this->SetAirAbsorptionFactor(1.0f);
525  }
526 
527  if (App::audio_enable_efx->getBool())
528  {
529  m_listener_efx_reverb_properties = this->GetReverbPresetAt(m_listener_position);
530  }
531  }
532 }
533 
535 {
536  float orientation[6];
537  // direction
538  orientation[0] = m_listener_direction.x;
539  orientation[1] = m_listener_direction.y;
540  orientation[2] = m_listener_direction.z;
541  // up
542  orientation[3] = m_listener_up.x;
543  orientation[4] = m_listener_up.y;
544  orientation[5] = m_listener_up.z;
545 
546  alListener3f(AL_POSITION, m_listener_position.x, m_listener_position.y, m_listener_position.z);
547  alListener3f(AL_VELOCITY, m_listener_velocity.x, m_listener_velocity.y, m_listener_velocity.z);
548  alListenerfv(AL_ORIENTATION, orientation);
549 }
550 
551 const EFXEAXREVERBPROPERTIES* SoundManager::GetReverbPresetAt(const Ogre::Vector3 position) const
552 {
553  // for the listener we do additional checks
554  if (position == m_listener_position)
555  {
556  if (!App::audio_force_listener_efx_preset->getStr().empty())
557  {
558  return this->GetEfxProperties(App::audio_force_listener_efx_preset->getStr());
559  }
560  }
561 
562  const auto water = App::GetGameContext()->GetTerrain()->getWater();
563  bool position_is_underwater = (water != nullptr ? water->IsUnderWater(position) : false);
564  if (position_is_underwater)
565  {
566  return this->GetEfxProperties("EFX_REVERB_PRESET_UNDERWATER");
567  }
568 
569  // check if position is inside a collision box with a reverb_preset assigned to it
570  for (const collision_box_t& collision_box : App::GetGameContext()->GetTerrain()->GetCollisions()->getCollisionBoxes())
571  {
572  if (!collision_box.reverb_preset_name.empty())
573  {
574  const Ogre::AxisAlignedBox collision_box_aab = Ogre::AxisAlignedBox(collision_box.lo, collision_box.hi);
575 
576  if (collision_box_aab.contains(position))
577  {
578  return this->GetEfxProperties(collision_box.reverb_preset_name);
579  }
580  }
581  }
582 
583  if (!App::audio_default_efx_preset->getStr().empty())
584  {
585  return this->GetEfxProperties(App::audio_default_efx_preset->getStr());
586  }
587  else
588  {
589  return nullptr;
590  }
591 }
592 
594 {
595  if (m_listener_efx_reverb_properties == nullptr)
596  {
597  this->SmoothlyUpdateAlAuxiliaryEffectSlot(dt, m_listener_slot, nullptr);
598  return;
599  }
600 
601  EFXEAXREVERBPROPERTIES current_environmental_properties = *m_listener_efx_reverb_properties;
602 
603  // early reflections panning, delay and strength
604  if (App::audio_enable_reflection_panning->getBool() && m_efx_reverb_engine == EfxReverbEngine::EAXREVERB)
605  {
606  std::tuple<Ogre::Vector3, float, float> target_early_reflections_properties = this->ComputeEarlyReflectionsProperties();
607 
608  // convert panning vector from RHS to EAXREVERB's LHS
609  current_environmental_properties.flReflectionsPan[0] = std::get<0>(target_early_reflections_properties).x;
610  current_environmental_properties.flReflectionsPan[1] = 0;
611  current_environmental_properties.flReflectionsPan[2] = -std::get<0>(target_early_reflections_properties).z;
612 
613  current_environmental_properties.flReflectionsGain = std::get<1>(target_early_reflections_properties);
614  current_environmental_properties.flReflectionsDelay = std::get<2>(target_early_reflections_properties);
615  }
616 
617  this->SmoothlyUpdateAlAuxiliaryEffectSlot(dt, m_listener_slot, &current_environmental_properties);
618 }
619 
620 void SoundManager::SmoothlyUpdateAlAuxiliaryEffectSlot(const float dt, const ALuint slot_id, const EFXEAXREVERBPROPERTIES* target_efx_properties)
621 {
622  const float time_to_target = 0.333f; // seconds to reach the target properties from the current properties
623  // ensure to not exceed limits of reverb parameters if timestep is too large
624  const float step = std::min(dt / time_to_target, 0.5f);
625  static std::map<ALuint, EFXEAXREVERBPROPERTIES> current_efx_properties_of_slot;
626 
627  if (target_efx_properties == nullptr)
628  {
629  this->alAuxiliaryEffectSloti(slot_id, AL_EFFECTSLOT_EFFECT, AL_EFFECTSLOT_NULL);
630  return;
631  }
632 
633  const auto it = current_efx_properties_of_slot.find(slot_id);
634  if (it == current_efx_properties_of_slot.end())
635  {
636  // previously unseen effect slot, set a starting point
637  current_efx_properties_of_slot[slot_id] = *target_efx_properties;
638  }
639 
640  ALuint efx_effect_id;
641  // create new AL effect if not existing
642  if (m_efx_effect_id_map.find(slot_id) == m_efx_effect_id_map.end())
643  {
644  efx_effect_id = this->CreateAlEffect(target_efx_properties);
645  m_efx_effect_id_map[slot_id] = efx_effect_id;
646  }
647  else
648  {
649  efx_effect_id = m_efx_effect_id_map.find(slot_id)->second;
650  }
651 
652  // compute intermediate step between current and target properties using linear interpolation based on time step
653  current_efx_properties_of_slot[slot_id] =
654  {
655  current_efx_properties_of_slot[slot_id].flDensity + step * (target_efx_properties->flDensity - current_efx_properties_of_slot[slot_id].flDensity),
656  current_efx_properties_of_slot[slot_id].flDiffusion + step * (target_efx_properties->flDiffusion - current_efx_properties_of_slot[slot_id].flDiffusion),
657  current_efx_properties_of_slot[slot_id].flGain + step * (target_efx_properties->flGain - current_efx_properties_of_slot[slot_id].flGain),
658  current_efx_properties_of_slot[slot_id].flGainHF + step * (target_efx_properties->flGainHF - current_efx_properties_of_slot[slot_id].flGainHF),
659  current_efx_properties_of_slot[slot_id].flGainLF + step * (target_efx_properties->flGainLF - current_efx_properties_of_slot[slot_id].flGainLF),
660  current_efx_properties_of_slot[slot_id].flDecayTime + step * (target_efx_properties->flDecayTime - current_efx_properties_of_slot[slot_id].flDecayTime),
661  current_efx_properties_of_slot[slot_id].flDecayHFRatio + step * (target_efx_properties->flDecayHFRatio - current_efx_properties_of_slot[slot_id].flDecayHFRatio),
662  current_efx_properties_of_slot[slot_id].flDecayLFRatio + step * (target_efx_properties->flDecayLFRatio - current_efx_properties_of_slot[slot_id].flDecayLFRatio),
663  current_efx_properties_of_slot[slot_id].flReflectionsGain + step * (target_efx_properties->flReflectionsGain - current_efx_properties_of_slot[slot_id].flReflectionsGain),
664  current_efx_properties_of_slot[slot_id].flReflectionsDelay + step * (target_efx_properties->flReflectionsDelay - current_efx_properties_of_slot[slot_id].flReflectionsDelay),
665  current_efx_properties_of_slot[slot_id].flReflectionsPan[0] + step * (target_efx_properties->flReflectionsPan[0] - current_efx_properties_of_slot[slot_id].flReflectionsPan[0]),
666  current_efx_properties_of_slot[slot_id].flReflectionsPan[1] + step * (target_efx_properties->flReflectionsPan[1] - current_efx_properties_of_slot[slot_id].flReflectionsPan[1]),
667  current_efx_properties_of_slot[slot_id].flReflectionsPan[2] + step * (target_efx_properties->flReflectionsPan[2] - current_efx_properties_of_slot[slot_id].flReflectionsPan[2]),
668  current_efx_properties_of_slot[slot_id].flLateReverbGain + step * (target_efx_properties->flLateReverbGain - current_efx_properties_of_slot[slot_id].flLateReverbGain),
669  current_efx_properties_of_slot[slot_id].flLateReverbDelay + step * (target_efx_properties->flLateReverbDelay - current_efx_properties_of_slot[slot_id].flLateReverbDelay),
670  current_efx_properties_of_slot[slot_id].flLateReverbPan[0] + step * (target_efx_properties->flLateReverbPan[0] - current_efx_properties_of_slot[slot_id].flLateReverbPan[0]),
671  current_efx_properties_of_slot[slot_id].flLateReverbPan[1] + step * (target_efx_properties->flLateReverbPan[1] - current_efx_properties_of_slot[slot_id].flLateReverbPan[1]),
672  current_efx_properties_of_slot[slot_id].flLateReverbPan[2] + step * (target_efx_properties->flLateReverbPan[2] - current_efx_properties_of_slot[slot_id].flLateReverbPan[2]),
673  current_efx_properties_of_slot[slot_id].flEchoTime + step * (target_efx_properties->flEchoTime - current_efx_properties_of_slot[slot_id].flEchoTime),
674  current_efx_properties_of_slot[slot_id].flEchoDepth + step * (target_efx_properties->flEchoDepth - current_efx_properties_of_slot[slot_id].flEchoDepth),
675  current_efx_properties_of_slot[slot_id].flModulationTime + step * (target_efx_properties->flModulationTime - current_efx_properties_of_slot[slot_id].flModulationTime),
676  current_efx_properties_of_slot[slot_id].flModulationDepth + step * (target_efx_properties->flModulationDepth - current_efx_properties_of_slot[slot_id].flModulationDepth),
677  current_efx_properties_of_slot[slot_id].flAirAbsorptionGainHF + step * (target_efx_properties->flAirAbsorptionGainHF - current_efx_properties_of_slot[slot_id].flAirAbsorptionGainHF),
678  current_efx_properties_of_slot[slot_id].flHFReference + step * (target_efx_properties->flHFReference - current_efx_properties_of_slot[slot_id].flHFReference),
679  current_efx_properties_of_slot[slot_id].flLFReference + step * (target_efx_properties->flLFReference - current_efx_properties_of_slot[slot_id].flLFReference),
680  current_efx_properties_of_slot[slot_id].flRoomRolloffFactor + step * (target_efx_properties->flRoomRolloffFactor - current_efx_properties_of_slot[slot_id].flRoomRolloffFactor),
681  static_cast<int>(std::round(current_efx_properties_of_slot[slot_id].iDecayHFLimit + step * (target_efx_properties->iDecayHFLimit - current_efx_properties_of_slot[slot_id].iDecayHFLimit))),
682  };
683 
684  // update AL effect to intermediate values
685  switch (m_efx_reverb_engine)
686  {
688  this->alEffectf( efx_effect_id, AL_EAXREVERB_DENSITY, current_efx_properties_of_slot[slot_id].flDensity);
689  this->alEffectf( efx_effect_id, AL_EAXREVERB_DIFFUSION, current_efx_properties_of_slot[slot_id].flDiffusion);
690  this->alEffectf( efx_effect_id, AL_EAXREVERB_GAIN, current_efx_properties_of_slot[slot_id].flGain);
691  this->alEffectf( efx_effect_id, AL_EAXREVERB_GAINHF, current_efx_properties_of_slot[slot_id].flGainHF);
692  this->alEffectf( efx_effect_id, AL_EAXREVERB_GAINLF, current_efx_properties_of_slot[slot_id].flGainLF);
693  this->alEffectf( efx_effect_id, AL_EAXREVERB_DECAY_TIME, current_efx_properties_of_slot[slot_id].flDecayTime);
694  this->alEffectf( efx_effect_id, AL_EAXREVERB_DECAY_HFRATIO, current_efx_properties_of_slot[slot_id].flDecayHFRatio);
695  this->alEffectf( efx_effect_id, AL_EAXREVERB_DECAY_LFRATIO, current_efx_properties_of_slot[slot_id].flDecayLFRatio);
696  this->alEffectf( efx_effect_id, AL_EAXREVERB_REFLECTIONS_GAIN, current_efx_properties_of_slot[slot_id].flReflectionsGain);
697  this->alEffectf( efx_effect_id, AL_EAXREVERB_REFLECTIONS_DELAY, current_efx_properties_of_slot[slot_id].flReflectionsDelay);
698  this->alEffectfv(efx_effect_id, AL_EAXREVERB_REFLECTIONS_PAN, current_efx_properties_of_slot[slot_id].flReflectionsPan);
699  this->alEffectf( efx_effect_id, AL_EAXREVERB_LATE_REVERB_GAIN, current_efx_properties_of_slot[slot_id].flLateReverbGain);
700  this->alEffectf( efx_effect_id, AL_EAXREVERB_LATE_REVERB_DELAY, current_efx_properties_of_slot[slot_id].flLateReverbDelay);
701  this->alEffectfv(efx_effect_id, AL_EAXREVERB_LATE_REVERB_PAN, current_efx_properties_of_slot[slot_id].flLateReverbPan);
702  this->alEffectf( efx_effect_id, AL_EAXREVERB_ECHO_TIME, current_efx_properties_of_slot[slot_id].flEchoTime);
703  this->alEffectf( efx_effect_id, AL_EAXREVERB_ECHO_DEPTH, current_efx_properties_of_slot[slot_id].flEchoDepth);
704  this->alEffectf( efx_effect_id, AL_EAXREVERB_MODULATION_TIME, current_efx_properties_of_slot[slot_id].flModulationTime);
705  this->alEffectf( efx_effect_id, AL_EAXREVERB_MODULATION_DEPTH, current_efx_properties_of_slot[slot_id].flModulationDepth);
706  this->alEffectf( efx_effect_id, AL_EAXREVERB_AIR_ABSORPTION_GAINHF, current_efx_properties_of_slot[slot_id].flAirAbsorptionGainHF);
707  this->alEffectf( efx_effect_id, AL_EAXREVERB_HFREFERENCE, current_efx_properties_of_slot[slot_id].flHFReference);
708  this->alEffectf( efx_effect_id, AL_EAXREVERB_LFREFERENCE, current_efx_properties_of_slot[slot_id].flLFReference);
709  this->alEffectf( efx_effect_id, AL_EAXREVERB_ROOM_ROLLOFF_FACTOR, current_efx_properties_of_slot[slot_id].flRoomRolloffFactor);
710  this->alEffecti( efx_effect_id, AL_EAXREVERB_DECAY_HFLIMIT, current_efx_properties_of_slot[slot_id].iDecayHFLimit);
711  break;
712 
714  this->alEffectf( efx_effect_id, AL_REVERB_DENSITY, current_efx_properties_of_slot[slot_id].flDensity);
715  this->alEffectf( efx_effect_id, AL_REVERB_DIFFUSION, current_efx_properties_of_slot[slot_id].flDiffusion);
716  this->alEffectf( efx_effect_id, AL_REVERB_GAIN, current_efx_properties_of_slot[slot_id].flGain);
717  this->alEffectf( efx_effect_id, AL_REVERB_GAINHF, current_efx_properties_of_slot[slot_id].flGainHF);
718  this->alEffectf( efx_effect_id, AL_REVERB_DECAY_TIME, current_efx_properties_of_slot[slot_id].flDecayTime);
719  this->alEffectf( efx_effect_id, AL_REVERB_DECAY_HFRATIO, current_efx_properties_of_slot[slot_id].flDecayHFRatio);
720  this->alEffectf( efx_effect_id, AL_REVERB_REFLECTIONS_GAIN, current_efx_properties_of_slot[slot_id].flReflectionsGain);
721  this->alEffectf( efx_effect_id, AL_REVERB_REFLECTIONS_DELAY, current_efx_properties_of_slot[slot_id].flReflectionsDelay);
722  this->alEffectf( efx_effect_id, AL_REVERB_LATE_REVERB_GAIN, current_efx_properties_of_slot[slot_id].flLateReverbGain);
723  this->alEffectf( efx_effect_id, AL_REVERB_LATE_REVERB_DELAY, current_efx_properties_of_slot[slot_id].flLateReverbDelay);
724  this->alEffectf( efx_effect_id, AL_REVERB_AIR_ABSORPTION_GAINHF, current_efx_properties_of_slot[slot_id].flAirAbsorptionGainHF);
725  this->alEffectf( efx_effect_id, AL_REVERB_ROOM_ROLLOFF_FACTOR, current_efx_properties_of_slot[slot_id].flRoomRolloffFactor);
726  this->alEffectf( efx_effect_id, AL_REVERB_DECAY_HFLIMIT, current_efx_properties_of_slot[slot_id].iDecayHFLimit);
727  break;
728 
730  this->alAuxiliaryEffectSloti(slot_id, AL_EFFECTSLOT_EFFECT, AL_EFFECTSLOT_NULL);
731  return;
732  }
733 
734  // make the slot use the updated AL effect
735  this->alAuxiliaryEffectSloti(slot_id, AL_EFFECTSLOT_EFFECT, efx_effect_id);
736 }
737 
738 std::tuple<Ogre::Vector3, float, float> SoundManager::ComputeEarlyReflectionsProperties() const
739 {
740  const float max_distance = 2.0f;
741  const float reflections_gain_boost_max = 2.0f; // 6.32 dB
742  float early_reflections_gain;
743  float early_reflections_delay;
744  float magnitude = 0;
745  Ogre::Vector3 early_reflections_pan = { 0.0f, 0.0f, 0.0f};
746 
747  /*
748  * To detect surfaces around the listener within the vicinity of
749  * max_distance, we cast rays counter-clockwise in a 360° circle
750  * around the listener on a horizontal plane realative to the listener.
751  */
752  bool nearby_surface_detected = false;
753  const float angle_step_size = 90;
754  float closest_surface_distance = std::numeric_limits<float>::max();
755 
756  for (float angle = 0; angle < 360; angle += angle_step_size)
757  {
758  float closest_surface_distance_in_this_direction = std::numeric_limits<float>::max();
759  Ogre::Vector3 raycast_direction = Quaternion(Ogre::Degree(angle), m_listener_up) * m_listener_direction;
760  raycast_direction.normalise();
761  // accompany direction vector for how the intersectsTris function works
762 
763  // check for nearby collision meshes
764  Ray ray = Ray(m_listener_position, raycast_direction * max_distance * App::GetGameContext()->GetTerrain()->GetCollisions()->GetCellSize());
765  std::pair<bool, Ogre::Real> intersection = App::GetGameContext()->GetTerrain()->GetCollisions()->intersectsTris(ray);
766 
767  if (intersection.first)
768  {
769  closest_surface_distance_in_this_direction = intersection.second * max_distance;
770  }
771 
772  ray.setDirection(ray.getDirection().normalisedCopy());
773 
774  // check for nearby collision boxes
775  for (const collision_box_t& collision_box : App::GetGameContext()->GetTerrain()->GetCollisions()->getCollisionBoxes())
776  {
777  if (!collision_box.enabled || collision_box.virt) { continue; }
778  intersection = ray.intersects(Ogre::AxisAlignedBox(collision_box.lo, collision_box.hi));
779  if (intersection.first && intersection.second <= max_distance)
780  {
781  closest_surface_distance_in_this_direction = std::min(closest_surface_distance_in_this_direction, intersection.second);
782  }
783  }
784 
785  // check for nearby actors
787  for (const ActorPtr& actor : actors)
788  {
789  // ignore own truck if player is driving one
790  if (actor == App::GetGameContext()->GetPlayerCharacter()->GetActorCoupling()) { continue; }
791 
792  intersection = ray.intersects(actor->ar_bounding_box);
793  if (intersection.first && intersection.second <= max_distance)
794  {
795  closest_surface_distance_in_this_direction = std::min(closest_surface_distance_in_this_direction, intersection.second);
796  }
797  }
798 
799  closest_surface_distance = std::min(closest_surface_distance, closest_surface_distance_in_this_direction);
800 
801  if (closest_surface_distance_in_this_direction <= max_distance)
802  {
803  early_reflections_pan += raycast_direction * (max_distance - closest_surface_distance_in_this_direction);
804  }
805  }
806 
807  nearby_surface_detected = closest_surface_distance <= max_distance;
808 
809  // TODO vertical raycasts
810 
811  if (!nearby_surface_detected)
812  {
813  // reset values to the original values of the preset
814  early_reflections_delay = m_listener_efx_reverb_properties->flReflectionsDelay;
815  early_reflections_gain = m_listener_efx_reverb_properties->flReflectionsGain;
816  }
817  else // at least one nearby surface was detected
818  {
819  // we assume that surfaces further away cause less focussed reflections
820  magnitude = 1.0f - early_reflections_pan.length() / Ogre::Math::Sqrt(2.0f * Ogre::Math::Pow(max_distance, 2));
821 
822  // set delay based on distance to the closest surface
823  early_reflections_delay = closest_surface_distance / this->GetSpeedOfSound();
824 
825  early_reflections_gain = std::min(
826  (m_listener_efx_reverb_properties->flReflectionsGain
827  + reflections_gain_boost_max
828  - (reflections_gain_boost_max * (magnitude))),
829  AL_EAXREVERB_MAX_REFLECTIONS_GAIN);
830  }
831 
832  // transform the pan vector from being listener-relative to being user-relative
833 
834  // determine the rotation of the listener direction from straight-ahead vector
835  // work around Quaternion quirks at around 180° rotation
836  Ogre::Quaternion horizontal_rotation;
837  if (m_listener_direction.z > 0.0f)
838  {
839  horizontal_rotation = Quaternion(Ogre::Degree(180), m_listener_up) * m_listener_direction.getRotationTo(Ogre::Vector3::UNIT_Z);
840  }
841  else
842  {
843  horizontal_rotation = m_listener_direction.getRotationTo(Ogre::Vector3::NEGATIVE_UNIT_Z);
844  }
845 
846  early_reflections_pan = horizontal_rotation * early_reflections_pan;
847  early_reflections_pan.normalise();
848 
849  early_reflections_pan = magnitude * early_reflections_pan;
850 
851  return std::make_tuple(early_reflections_pan, early_reflections_gain, early_reflections_delay);
852 }
853 
854 ALuint SoundManager::CreateAlEffect(const EFXEAXREVERBPROPERTIES* efx_properties) const
855 {
856  ALuint effect = 0;
857  ALenum error;
858 
859  this->alGenEffects(1, &effect);
860 
861  switch (m_efx_reverb_engine)
862  {
864  this->alEffecti( effect, AL_EFFECT_TYPE, AL_EFFECT_EAXREVERB);
865 
866  this->alEffectf( effect, AL_EAXREVERB_DENSITY, efx_properties->flDensity);
867  this->alEffectf( effect, AL_EAXREVERB_DIFFUSION, efx_properties->flDiffusion);
868  this->alEffectf( effect, AL_EAXREVERB_GAIN, efx_properties->flGain);
869  this->alEffectf( effect, AL_EAXREVERB_GAINHF, efx_properties->flGainHF);
870  this->alEffectf( effect, AL_EAXREVERB_GAINLF, efx_properties->flGainLF);
871  this->alEffectf( effect, AL_EAXREVERB_DECAY_TIME, efx_properties->flDecayTime);
872  this->alEffectf( effect, AL_EAXREVERB_DECAY_HFRATIO, efx_properties->flDecayHFRatio);
873  this->alEffectf( effect, AL_EAXREVERB_DECAY_LFRATIO, efx_properties->flDecayLFRatio);
874  this->alEffectf( effect, AL_EAXREVERB_REFLECTIONS_GAIN, efx_properties->flReflectionsGain);
875  this->alEffectf( effect, AL_EAXREVERB_REFLECTIONS_DELAY, efx_properties->flReflectionsDelay);
876  this->alEffectfv(effect, AL_EAXREVERB_REFLECTIONS_PAN, efx_properties->flReflectionsPan);
877  this->alEffectf( effect, AL_EAXREVERB_LATE_REVERB_GAIN, efx_properties->flLateReverbGain);
878  this->alEffectf( effect, AL_EAXREVERB_LATE_REVERB_DELAY, efx_properties->flLateReverbDelay);
879  this->alEffectfv(effect, AL_EAXREVERB_LATE_REVERB_PAN, efx_properties->flLateReverbPan);
880  this->alEffectf( effect, AL_EAXREVERB_ECHO_TIME, efx_properties->flEchoTime);
881  this->alEffectf( effect, AL_EAXREVERB_ECHO_DEPTH, efx_properties->flEchoDepth);
882  this->alEffectf( effect, AL_EAXREVERB_MODULATION_TIME, efx_properties->flModulationTime);
883  this->alEffectf( effect, AL_EAXREVERB_MODULATION_DEPTH, efx_properties->flModulationDepth);
884  this->alEffectf( effect, AL_EAXREVERB_AIR_ABSORPTION_GAINHF, efx_properties->flAirAbsorptionGainHF);
885  this->alEffectf( effect, AL_EAXREVERB_HFREFERENCE, efx_properties->flHFReference);
886  this->alEffectf( effect, AL_EAXREVERB_LFREFERENCE, efx_properties->flLFReference);
887  this->alEffectf( effect, AL_EAXREVERB_ROOM_ROLLOFF_FACTOR, efx_properties->flRoomRolloffFactor);
888  this->alEffecti( effect, AL_EAXREVERB_DECAY_HFLIMIT, efx_properties->iDecayHFLimit);
889 
890  break;
892  this->alEffecti(effect, AL_EFFECT_TYPE, AL_EFFECT_REVERB);
893 
894  this->alEffectf(effect, AL_REVERB_DENSITY, efx_properties->flDensity);
895  this->alEffectf(effect, AL_REVERB_DIFFUSION, efx_properties->flDiffusion);
896  this->alEffectf(effect, AL_REVERB_GAIN, efx_properties->flGain);
897  this->alEffectf(effect, AL_REVERB_GAINHF, efx_properties->flGainHF);
898  this->alEffectf(effect, AL_REVERB_DECAY_TIME, efx_properties->flDecayTime);
899  this->alEffectf(effect, AL_REVERB_DECAY_HFRATIO, efx_properties->flDecayHFRatio);
900  this->alEffectf(effect, AL_REVERB_REFLECTIONS_GAIN, efx_properties->flReflectionsGain);
901  this->alEffectf(effect, AL_REVERB_REFLECTIONS_DELAY, efx_properties->flReflectionsDelay);
902  this->alEffectf(effect, AL_REVERB_LATE_REVERB_GAIN, efx_properties->flLateReverbGain);
903  this->alEffectf(effect, AL_REVERB_LATE_REVERB_DELAY, efx_properties->flLateReverbDelay);
904  this->alEffectf(effect, AL_REVERB_AIR_ABSORPTION_GAINHF, efx_properties->flAirAbsorptionGainHF);
905  this->alEffectf(effect, AL_REVERB_ROOM_ROLLOFF_FACTOR, efx_properties->flRoomRolloffFactor);
906  this->alEffecti(effect, AL_REVERB_DECAY_HFLIMIT, efx_properties->iDecayHFLimit);
907 
908  break;
910  default:
911  LOG("SoundManager: No usable reverb engine set, not creating reverb effect");
912  }
913 
914  error = alGetError();
915  if (error != AL_NO_ERROR)
916  {
917  LOG("SoundManager: Could not create EFX effect:" + TOSTRING(alGetString(error)));
918 
919  if (this->alIsEffect(effect))
920  this->alDeleteEffects(1, &effect);
921  return 0;
922  }
923 
924  return effect;
925 }
926 
927 void SoundManager::DeleteAlEffect(const ALuint efx_effect_id) const
928 {
929  ALenum error;
930  alGetError();
931 
932  this->alDeleteEffects(1, &efx_effect_id);
933 
934  error = alGetError();
935  if (error != AL_NO_ERROR)
936  {
937  LOG("SoundManager: Could not delete EFX effect: " + TOSTRING(alGetString(error)));
938  }
939 }
940 
941 bool compareByAudibility(std::pair<int, float> a, std::pair<int, float> b)
942 {
943  return a.second > b.second;
944 }
945 
946 // called when the camera moves
948 {
949  // Creates this issue: https://github.com/RigsOfRods/rigs-of-rods/issues/1054
950 #if 0
951  if (!audio_device) return;
952 
953  for (int i=0; i < m_audio_sources_in_use_count; i++)
954  {
955  audio_sources[i]->computeAudibility(m_listener_position);
956  audio_sources_most_audible[i].first = i;
957  audio_sources_most_audible[i].second = audio_sources[i]->audibility;
958  }
959  // sort first 'num_hardware_sources' sources by audibility
960  // see: https://en.wikipedia.org/wiki/Selection_algorithm
961  if ((m_audio_sources_in_use_count - 1) > hardware_sources_num)
962  {
963  std::nth_element(audio_sources_most_audible, audio_sources_most_audible+hardware_sources_num, audio_sources_most_audible + m_audio_sources_in_use_count - 1, compareByAudibility);
964  }
965  // retire out of range sources first
966  for (int i=0; i < m_audio_sources_in_use_count; i++)
967  {
968  if (audio_sources[audio_sources_most_audible[i].first]->hardware_index != -1 && (i >= hardware_sources_num || audio_sources_most_audible[i].second == 0))
969  retire(audio_sources_most_audible[i].first);
970  }
971  // assign new sources
972  for (int i=0; i < std::min(m_audio_sources_in_use_count, hardware_sources_num); i++)
973  {
974  if (audio_sources[audio_sources_most_audible[i].first]->hardware_index == -1 && audio_sources_most_audible[i].second > 0)
975  {
976  for (int j=0; j < hardware_sources_num; j++)
977  {
978  if (hardware_sources_map[j] == -1)
979  {
980  assign(audio_sources_most_audible[i].first, j);
981  break;
982  }
983  }
984  }
985  }
986 #endif
987 }
988 
989 void SoundManager::UpdateSourceFilters(const int hardware_index) const
990 {
991  bool source_is_obstructed = this->IsHardwareSourceObstructed(hardware_index);
992 
993  this->UpdateObstructionFilter(hardware_index, source_is_obstructed);
994 
995  /*
996  * If an obstruction was detected, also check for occlusions.
997  * The current implementation ignores the case of exclusion since
998  * it compares the reverb environments of the source and listener.
999  * This would not be enough to reliably detect exclusion in outdoor environments.
1000  */
1001  if (source_is_obstructed)
1002  {
1003  this->UpdateOcclusionFilter(hardware_index, m_listener_slot, m_listener_efx_reverb_properties);
1004  }
1005  else
1006  {
1007  // disable occlusion filter just in case it was previously active
1008  alSource3i(hardware_sources[hardware_index], AL_AUXILIARY_SEND_FILTER, m_listener_slot, m_efx_occlusion_wet_path_send_id, AL_FILTER_NULL);
1009  }
1010 }
1011 
1012 bool SoundManager::IsHardwareSourceObstructed(const int hardware_index) const
1013 {
1014  if ( hardware_sources_map[hardware_index] == -1 // no sound assigned to hardware source
1015  || App::app_state->getEnum<AppState>() != AppState::SIMULATION) // this is necessary to prevent a crash with enabled main menu music
1016  { return false; }
1017 
1018  /*
1019  * There is no simple way to know whether a truck has a closed cabin or not; hence
1020  * provide an option to always force obstruction if the player is inside a vehicle.
1021  */
1022  if
1023  (
1025  && App::GetGameContext()->GetPlayerCharacter()->GetActorCoupling() != nullptr
1026  && App::GetGameContext()->GetPlayerCharacter()->GetActorCoupling()->ar_bounding_box.contains(m_listener_position)
1027  )
1028  {
1029  return true;
1030  }
1031 
1032  /*
1033  * Perform various line of sight checks until either a collision was detected
1034  * and the filter has to be applied or no obstruction was detected.
1035  */
1036 
1037  std::pair<bool, Ogre::Real> intersection;
1038  const SoundPtr& corresponding_sound = audio_sources[hardware_sources_map[hardware_index]];
1039  const Ogre::Vector3 direction_to_sound = corresponding_sound->getPosition() - m_listener_position;
1040  const Ogre::Ray direct_path_to_sound = Ray(m_listener_position, direction_to_sound);
1041  bool obstruction_detected = false;
1042 
1043  // perform line of sight check against collision meshes
1044  // for this to work correctly, the direction vector of the ray must have
1045  // the length of the distance from the listener to the sound
1046  intersection = App::GetGameContext()->GetTerrain()->GetCollisions()->intersectsTris(direct_path_to_sound);
1047  obstruction_detected = intersection.first;
1048 
1049  if (!obstruction_detected)
1050  {
1051  // perform line of sight check agains collision boxes
1052  for (const collision_box_t& collision_box : App::GetGameContext()->GetTerrain()->GetCollisions()->getCollisionBoxes())
1053  {
1054  if (!collision_box.enabled || collision_box.virt) { continue; }
1055 
1056  Ogre::AxisAlignedBox collision_box_aab = Ogre::AxisAlignedBox(collision_box.lo, collision_box.hi);
1057 
1058  // Skip cases where the obstruction filter detection becomes unstable
1059  if ( collision_box_aab.contains(corresponding_sound->getPosition())
1060  || collision_box_aab.distance(corresponding_sound->getPosition()) < 0.1f)
1061  {
1062  continue;
1063  }
1064 
1065  intersection = direct_path_to_sound.intersects(collision_box_aab);
1066  obstruction_detected = intersection.first && intersection.second <= 1.0f;
1067  if (obstruction_detected)
1068  {
1069  break;
1070  }
1071  }
1072  }
1073 
1074  if (!obstruction_detected)
1075  {
1076  // perform line of sight check against actors
1077  const ActorPtrVec& actors = App::GetGameContext()->GetActorManager()->GetActors();
1078  bool soundsource_belongs_to_current_actor = false;
1079  for (const ActorPtr& actor : actors)
1080  {
1081  // Trucks shouldn't obstruct their own sound sources since the
1082  // obstruction is most likely already contained in the recording.
1083  for (int soundsource_index = 0; soundsource_index < actor->ar_num_soundsources; ++soundsource_index)
1084  {
1085  const soundsource_t& soundsource = actor->ar_soundsources[soundsource_index];
1086  const int num_sounds = soundsource.ssi->getTemplate()->getNumSounds();
1087  for (int num_sound = 0; num_sound < num_sounds; ++num_sound)
1088  {
1089  if (soundsource.ssi->getSound(num_sound) == corresponding_sound)
1090  {
1091  soundsource_belongs_to_current_actor = true;
1092  }
1093  }
1094  if (soundsource_belongs_to_current_actor) { break; }
1095  }
1096 
1097  if (soundsource_belongs_to_current_actor)
1098  {
1099  continue;
1100  }
1101 
1102  intersection = direct_path_to_sound.intersects(actor->ar_bounding_box);
1103  obstruction_detected = intersection.first && intersection.second <= 1.0f;
1104  if (obstruction_detected)
1105  {
1106  break;
1107  }
1108  }
1109  }
1110 
1111  if (!obstruction_detected)
1112  {
1113  // perform line of sight check against terrain
1114  intersection = App::GetGameContext()->GetTerrain()->GetCollisions()->intersectsTerrain(direct_path_to_sound);
1115  obstruction_detected = intersection.first;
1116  }
1117 
1118  return obstruction_detected;
1119 }
1120 
1121 void SoundManager::UpdateObstructionFilter(const int hardware_index, const bool enable_obstruction_filter) const
1122 {
1123  if (!App::audio_enable_obstruction->getBool() || !enable_obstruction_filter)
1124  {
1125  // detach the obstruction filter in case it was attached when the feature was previously enabled
1126  alSourcei(hardware_sources[hardware_index], AL_DIRECT_FILTER, AL_FILTER_NULL);
1127  }
1128  else {
1129  // Apply obstruction filter to the source
1130  alSourcei(hardware_sources[hardware_index], AL_DIRECT_FILTER, m_efx_outdoor_obstruction_lowpass_filter_id);
1131  }
1132 }
1133 
1134 bool SoundManager::UpdateOcclusionFilter(const int hardware_index, const ALuint effect_slot_id, const EFXEAXREVERBPROPERTIES* reference_efx_reverb_properties) const
1135 {
1136  if (hardware_sources_map[hardware_index] == -1) { return false; } // no sound assigned to hardware source
1137 
1138  if (!App::audio_enable_occlusion->getBool())
1139  {
1140  // detach the occlusion filter in case it was attached when the feature was previously enabled
1141  alSource3i(hardware_sources[hardware_index], AL_AUXILIARY_SEND_FILTER, effect_slot_id, m_efx_occlusion_wet_path_send_id, AL_FILTER_NULL);
1142  return false;
1143  }
1144 
1145  const SoundPtr& corresponding_sound = audio_sources[hardware_sources_map[hardware_index]];
1146  bool occlusion_detected = this->GetReverbPresetAt(corresponding_sound->getPosition()) != reference_efx_reverb_properties;
1147 
1148  if (occlusion_detected)
1149  {
1150  alSource3i(hardware_sources[hardware_index], AL_AUXILIARY_SEND_FILTER, effect_slot_id, m_efx_occlusion_wet_path_send_id, m_efx_occlusion_wet_path_lowpass_filter_id);
1151  }
1152  else
1153  {
1154  alSource3i(hardware_sources[hardware_index], AL_AUXILIARY_SEND_FILTER, effect_slot_id, m_efx_occlusion_wet_path_send_id, AL_FILTER_NULL);
1155  }
1156 
1157  return occlusion_detected;
1158 }
1159 
1161 {
1162  for (int hardware_index = 0; hardware_index < hardware_sources_num; hardware_index++)
1163  {
1164  if (hardware_sources_map[hardware_index] == -1) { continue;; } // no sound assigned to hardware source at this index
1165 
1166  const SoundPtr& corresponding_sound = audio_sources[hardware_sources_map[hardware_index]];
1167  const ActorPtrVec& actors = App::GetGameContext()->GetActorManager()->GetActors();
1168 
1169  for (const ActorPtr& actor : actors)
1170  {
1171  NodeNum_t sound_node = RoR::NODENUM_INVALID;
1172 
1173  // check if the sound corresponding to this hardware source belongs to the actor
1174  for (int soundsource_index = 0; soundsource_index < actor->ar_num_soundsources; ++soundsource_index)
1175  {
1176  const soundsource_t& soundsource = actor->ar_soundsources[soundsource_index];
1177  const int num_sounds = soundsource.ssi->getTemplate()->getNumSounds();
1178  for (int num_sound = 0; num_sound < num_sounds; ++num_sound)
1179  {
1180  if (soundsource.ssi->getSound(num_sound) == corresponding_sound)
1181  {
1182  sound_node = soundsource.nodenum;
1183  break;
1184  }
1185  }
1186  }
1187 
1188  if ( sound_node == RoR::NODENUM_INVALID // if the sound does not belong to a node of the current actor, there is no need for further checks
1189  || sound_node == 0) // node 0 might have several default (and undirected) sounds assigned, so skip it
1190  {
1191  continue;
1192  }
1193 
1194  // Update directivity if the sound corresponding to the hardware source is attached to an exhaust node of the actor
1195  const std::vector<Exhaust>& exhausts = actor->GetGfxActor()->getExhausts();
1196  for (const Exhaust& exhaust : exhausts)
1197  {
1198  if ( sound_node == exhaust.emitterNode
1199  || sound_node == exhaust.directionNode)
1200  {
1201  const Ogre::Vector3 emitter_node_pos = actor->getNodePosition(exhaust.emitterNode);
1202  const Ogre::Vector3 direction_node_pos = actor->getNodePosition(exhaust.directionNode);
1203 
1204  this->UpdateConeProperties(
1205  hardware_sources[hardware_index],
1206  emitter_node_pos - direction_node_pos,
1207  60.0f,
1208  170.0f,
1209  0.85f,
1210  0.80f);
1211 
1212  break;
1213  }
1214  }
1215 
1216  switch(actor->getTruckType())
1217  {
1218  case ActorType::AIRPLANE:
1219  // Update directivity if the sound corresponding to the hardware source is attached to an AeroEngine
1220  for (int engine_num = 0; engine_num < actor->ar_num_aeroengines; ++engine_num)
1221  {
1222  const auto& aero_engine = actor->ar_aeroengines[engine_num];
1223 
1224  switch(aero_engine->getType())
1225  {
1227  if ( sound_node == aero_engine->getNoderef()
1228  || sound_node == aero_engine->GetBackNode())
1229  {
1230  const Ogre::Vector3 aero_engine_ref_node = actor->getNodePosition(aero_engine->getNoderef());
1231  const Ogre::Vector3 aero_engine_back_node = actor->getNodePosition(aero_engine->GetBackNode());
1232 
1233  this->UpdateConeProperties(
1234  hardware_sources[hardware_index],
1235  aero_engine_ref_node - aero_engine_back_node,
1236  170.0f,
1237  270.0f,
1238  0.85f,
1239  0.70f);
1240  }
1241 
1242  break;
1243 
1245  /*
1246  * Since turbojets currently have no high-pitched noise sounds
1247  * for the air intake, we currently assume all sounds are
1248  * directed rearwards of the engine.
1249  * Should air intake noises be added, a front-directed cone should
1250  * be set for them with significant high-frequency dropoff outside.
1251  */
1252 
1253  if ( sound_node == aero_engine->getNoderef()
1254  || sound_node == aero_engine->GetFrontNode())
1255  {
1256  const Ogre::Vector3 aero_engine_ref_node = actor->getNodePosition(aero_engine->getNoderef());
1257  const Ogre::Vector3 aero_engine_front_node = actor->getNodePosition(aero_engine->GetFrontNode());
1258 
1259  this->UpdateConeProperties(
1260  hardware_sources[hardware_index],
1261  aero_engine_ref_node - aero_engine_front_node,
1262  60.0f,
1263  240.0f,
1264  0.60f,
1265  0.60f);
1266  }
1267 
1268  break;
1269 
1270  default: continue;
1271  }
1272  }
1273  break;
1274 
1275  case ActorType::BOAT:
1276  // Update directivity if the sound corresponding to the hardware source is attached to a Screwprop
1277  for (int screwprop_num = 0; screwprop_num < actor->ar_num_screwprops; ++screwprop_num)
1278  {
1279  const auto& screwprop = actor->ar_screwprops[screwprop_num];
1280 
1281  if ( sound_node == screwprop->GetRefNode()
1282  || sound_node == screwprop->GetBackNode())
1283  {
1284  const Ogre::Vector3 screwprop_ref_node = actor->getNodePosition(screwprop->GetRefNode());
1285  const Ogre::Vector3 screwprop_back_node = actor->getNodePosition(screwprop->GetBackNode());
1286 
1287  this->UpdateConeProperties(
1288  hardware_sources[hardware_index],
1289  screwprop_ref_node - screwprop_back_node,
1290  70.0f,
1291  170.0f,
1292  0.80f,
1293  0.70f);
1294 
1295  break;
1296  }
1297  }
1298  break;
1299 
1300  default: continue;
1301  }
1302  }
1303  }
1304 }
1305 
1307  const ALuint source,
1308  const Ogre::Vector3& cone_direction,
1309  const float cone_inner_angle,
1310  const float cone_outer_angle,
1311  const float cone_outer_gain,
1312  const float cone_outer_gain_hf
1313  ) const
1314 {
1315  alSource3f(source, AL_DIRECTION, cone_direction.x, cone_direction.y, cone_direction.z);
1316 
1317  alSourcef (source, AL_CONE_INNER_ANGLE, cone_inner_angle);
1318  alSourcef (source, AL_CONE_OUTER_ANGLE, cone_outer_angle);
1319  alSourcef (source, AL_CONE_OUTER_GAIN, cone_outer_gain);
1320 
1321  if (App::audio_enable_efx->getBool())
1322  {
1323  alSourcef(source, AL_CONE_OUTER_GAINHF, cone_outer_gain_hf);
1324  }
1325 }
1326 
1327 void SoundManager::recomputeSource(int source_index, int reason, float vfl, Vector3* vvec)
1328 {
1329  if (!audio_device)
1330  return;
1331  audio_sources[source_index]->computeAudibility(m_listener_position);
1332 
1333  if (audio_sources[source_index]->audibility == 0.0f)
1334  {
1335  if (audio_sources[source_index]->hardware_index != -1)
1336  {
1337  // retire the source if it is currently assigned
1338  retire(source_index);
1339  }
1340  }
1341  else
1342  {
1343  // this is a potentially audible m_audio_sources[source_index]
1344  if (audio_sources[source_index]->hardware_index != -1)
1345  {
1346  ALuint hw_source = hardware_sources[audio_sources[source_index]->hardware_index];
1347  // m_audio_sources[source_index] already playing
1348  // update the AL settings
1349  switch (reason)
1350  {
1351  case Sound::REASON_PLAY:
1352  this->UpdateSourceFilters(audio_sources[source_index]->hardware_index);
1353  alSourcePlay(hw_source);
1354  break;
1355  case Sound::REASON_STOP: alSourceStop(hw_source);
1356  break;
1357  case Sound::REASON_GAIN: alSourcef(hw_source, AL_GAIN, vfl * App::audio_master_volume->getFloat());
1358  break;
1359  case Sound::REASON_LOOP: alSourcei(hw_source, AL_LOOPING, (vfl > 0.5) ? AL_TRUE : AL_FALSE);
1360  break;
1361  case Sound::REASON_PTCH: alSourcef(hw_source, AL_PITCH, vfl);
1362  break;
1363  case Sound::REASON_POSN: alSource3f(hw_source, AL_POSITION, vvec->x, vvec->y, vvec->z);
1364  break;
1365  case Sound::REASON_VLCT: alSource3f(hw_source, AL_VELOCITY, vvec->x, vvec->y, vvec->z);
1366  break;
1367  default: break;
1368  }
1369  }
1370  else
1371  {
1372  // try to make it play by the hardware
1373  // check if there is one free m_audio_sources[source_index] in the pool
1374  if (hardware_sources_in_use_count < hardware_sources_num)
1375  {
1376  for (int i = 0; i < hardware_sources_num; i++)
1377  {
1378  if (hardware_sources_map[i] == -1)
1379  {
1380  assign(source_index, i);
1381  break;
1382  }
1383  }
1384  }
1385  else
1386  {
1387  // now, compute who is the faintest
1388  // note: we know the table m_hardware_sources_map is full!
1389  float fv = 1.0f;
1390  int al_faintest = 0;
1391  for (int i = 0; i < hardware_sources_num; i++)
1392  {
1393  if (hardware_sources_map[i] >= 0 && audio_sources[hardware_sources_map[i]]->audibility < fv)
1394  {
1395  fv = audio_sources[hardware_sources_map[i]]->audibility;
1396  al_faintest = i;
1397  }
1398  }
1399  // check to ensure that the sound is louder than the faintest sound currently playing
1400  if (fv < audio_sources[source_index]->audibility)
1401  {
1402  // this new m_audio_sources[source_index] is louder than the faintest!
1403  retire(hardware_sources_map[al_faintest]);
1404  assign(source_index, al_faintest);
1405  }
1406  // else this m_audio_sources[source_index] is too faint, we don't play it!
1407  }
1408  }
1409  }
1410 }
1411 
1412 void SoundManager::assign(int source_index, int hardware_index)
1413 {
1414  if (!audio_device)
1415  return;
1416  audio_sources[source_index]->hardware_index = hardware_index;
1417  hardware_sources_map[hardware_index] = source_index;
1418 
1419  ALuint hw_source = hardware_sources[hardware_index];
1420  SoundPtr& audio_source = audio_sources[source_index];
1421 
1422  // the hardware source is supposed to be stopped!
1423  alSourcei(hw_source, AL_BUFFER, audio_source->buffer);
1424  alSourcef(hw_source, AL_GAIN, audio_source->gain * App::audio_master_volume->getFloat());
1425  alSourcei(hw_source, AL_LOOPING, (audio_source->loop) ? AL_TRUE : AL_FALSE);
1426  alSourcef(hw_source, AL_PITCH, audio_source->pitch);
1427  alSource3f(hw_source, AL_POSITION, audio_source->position.x, audio_source->position.y, audio_source->position.z);
1428  alSource3f(hw_source, AL_VELOCITY, audio_source->velocity.x, audio_source->velocity.y, audio_source->velocity.z);
1429 
1430  if (audio_source->should_play)
1431  {
1432  this->UpdateSourceFilters(audio_sources[source_index]->hardware_index);
1433  alSourcePlay(hw_source);
1434  }
1435 
1436  hardware_sources_in_use_count++;
1437 }
1438 
1439 void SoundManager::retire(int source_index)
1440 {
1441  if (!audio_device)
1442  return;
1443  if (audio_sources[source_index]->hardware_index == -1)
1444  return;
1445  alSourceStop(hardware_sources[audio_sources[source_index]->hardware_index]);
1446  hardware_sources_map[audio_sources[source_index]->hardware_index] = -1;
1447  audio_sources[source_index]->hardware_index = -1;
1448  hardware_sources_in_use_count--;
1449 }
1450 
1452 {
1453  if (!audio_device)
1454  return;
1455  // no mutex needed
1456  alListenerf(AL_GAIN, 0.0f);
1457 }
1458 
1460 {
1461  if (!audio_device)
1462  return;
1463  // no mutex needed
1464  alListenerf(AL_GAIN, App::audio_master_volume->getFloat());
1465 }
1466 
1468 {
1469  if (!audio_device)
1470  return;
1471  // no mutex needed
1472  App::audio_master_volume->setVal(v); // TODO: Use 'pending' mechanism and set externally, only 'apply' here.
1473  alListenerf(AL_GAIN, v);
1474 }
1475 
1476 SoundPtr SoundManager::createSound(String filename, Ogre::String resource_group_name /* = "" */)
1477 {
1478  if (!audio_device)
1479  return NULL;
1480 
1481  if (audio_buffers_in_use_count >= MAX_AUDIO_BUFFERS)
1482  {
1483  LOG("SoundManager: Reached MAX_AUDIO_BUFFERS limit (" + TOSTRING(MAX_AUDIO_BUFFERS) + ")");
1484  return NULL;
1485  }
1486 
1487  ALuint buffer = 0;
1488 
1489  // is the file already loaded?
1490  for (int i = 0; i < audio_buffers_in_use_count; i++)
1491  {
1492  if (filename == audio_buffer_file_name[i])
1493  {
1494  buffer = audio_buffers[i];
1495  break;
1496  }
1497  }
1498 
1499  if (!buffer)
1500  {
1501  // load the file
1502  alGenBuffers(1, &audio_buffers[audio_buffers_in_use_count]);
1503  if (loadWAVFile(filename, audio_buffers[audio_buffers_in_use_count], resource_group_name))
1504  {
1505  // there was an error!
1506  alDeleteBuffers(1, &audio_buffers[audio_buffers_in_use_count]);
1507  audio_buffer_file_name[audio_buffers_in_use_count] = "";
1508  return NULL;
1509  }
1510  buffer = audio_buffers[audio_buffers_in_use_count];
1511  audio_buffer_file_name[audio_buffers_in_use_count] = filename;
1512  audio_buffers_in_use_count++;
1513  }
1514 
1515  audio_sources[m_audio_sources_in_use_count] = new Sound(buffer, this, m_audio_sources_in_use_count);
1516 
1517  return audio_sources[m_audio_sources_in_use_count++];
1518 }
1519 
1520 bool SoundManager::loadWAVFile(String filename, ALuint buffer, Ogre::String resource_group_name /*= ""*/)
1521 {
1522  if (!audio_device)
1523  return true;
1524  LOG("Loading WAV file "+filename);
1525 
1526  // create the Stream
1527  ResourceGroupManager* rgm = ResourceGroupManager::getSingletonPtr();
1528  if (resource_group_name == "")
1529  {
1530  resource_group_name = rgm->findGroupContainingResource(filename);
1531  }
1532  DataStreamPtr stream = rgm->openResource(filename, resource_group_name);
1533 
1534  // load RIFF/WAVE
1535  char magic[5];
1536  magic[4] = 0;
1537  unsigned int lbuf; // uint32_t
1538  unsigned short sbuf; // uint16_t
1539 
1540  // check magic
1541  if (stream->read(magic, 4) != 4)
1542  {
1543  LOG("Could not read file "+filename);
1544  return true;
1545  }
1546  if (String(magic) != String("RIFF"))
1547  {
1548  LOG("Invalid WAV file (no RIFF): "+filename);
1549  return true;
1550  }
1551  // skip 4 bytes (magic)
1552  stream->skip(4);
1553  // check file format
1554  if (stream->read(magic, 4) != 4)
1555  {
1556  LOG("Could not read file "+filename);
1557  return true;
1558  }
1559  if (String(magic) != String("WAVE"))
1560  {
1561  LOG("Invalid WAV file (no WAVE): "+filename);
1562  return true;
1563  }
1564  // check 'fmt ' sub chunk (1)
1565  if (stream->read(magic, 4) != 4)
1566  {
1567  LOG("Could not read file "+filename);
1568  return true;
1569  }
1570  if (String(magic) != String("fmt "))
1571  {
1572  LOG("Invalid WAV file (no fmt): "+filename);
1573  return true;
1574  }
1575  // read (1)'s size
1576  if (stream->read(&lbuf, 4) != 4)
1577  {
1578  LOG("Could not read file "+filename);
1579  return true;
1580  }
1581  unsigned long subChunk1Size = lbuf;
1582  if (subChunk1Size < 16)
1583  {
1584  LOG("Invalid WAV file (invalid subChunk1Size): "+filename);
1585  return true;
1586  }
1587  // check PCM audio format
1588  if (stream->read(&sbuf, 2) != 2)
1589  {
1590  LOG("Could not read file "+filename);
1591  return true;
1592  }
1593  unsigned short audioFormat = sbuf;
1594  if (audioFormat != 1)
1595  {
1596  LOG("Invalid WAV file (invalid audioformat "+TOSTRING(audioFormat)+"): "+filename);
1597  return true;
1598  }
1599  // read number of channels
1600  if (stream->read(&sbuf, 2) != 2)
1601  {
1602  LOG("Could not read file "+filename);
1603  return true;
1604  }
1605  unsigned short channels = sbuf;
1606  // read frequency (sample rate)
1607  if (stream->read(&lbuf, 4) != 4)
1608  {
1609  LOG("Could not read file "+filename);
1610  return true;
1611  }
1612  unsigned long freq = lbuf;
1613  // skip 6 bytes (Byte rate (4), Block align (2))
1614  stream->skip(6);
1615  // read bits per sample
1616  if (stream->read(&sbuf, 2) != 2)
1617  {
1618  LOG("Could not read file "+filename);
1619  return true;
1620  }
1621  unsigned short bps = sbuf;
1622  // check 'data' sub chunk (2)
1623  if (stream->read(magic, 4) != 4)
1624  {
1625  LOG("Could not read file "+filename);
1626  return true;
1627  }
1628  if (String(magic) != String("data") && String(magic) != String("fact"))
1629  {
1630  LOG("Invalid WAV file (no data/fact): "+filename);
1631  return true;
1632  }
1633  // fact is an option section we don't need to worry about
1634  if (String(magic) == String("fact"))
1635  {
1636  stream->skip(8);
1637  // now we should hit the data chunk
1638  if (stream->read(magic, 4) != 4)
1639  {
1640  LOG("Could not read file "+filename);
1641  return true;
1642  }
1643  if (String(magic) != String("data"))
1644  {
1645  LOG("Invalid WAV file (no data): "+filename);
1646  return true;
1647  }
1648  }
1649  // the next four bytes are the remaining size of the file
1650  if (stream->read(&lbuf, 4) != 4)
1651  {
1652  LOG("Could not read file "+filename);
1653  return true;
1654  }
1655 
1656  unsigned long dataSize = lbuf;
1657  int format = 0;
1658 
1659  if (channels == 1 && bps == 8)
1660  format = AL_FORMAT_MONO8;
1661  else if (channels == 1 && bps == 16)
1662  format = AL_FORMAT_MONO16;
1663  else if (channels == 2 && bps == 8)
1664  format = AL_FORMAT_STEREO16;
1665  else if (channels == 2 && bps == 16)
1666  format = AL_FORMAT_STEREO16;
1667  else
1668  {
1669  LOG("Invalid WAV file (wrong channels/bps): "+filename);
1670  return true;
1671  }
1672 
1673  if (channels != 1) LOG("Invalid WAV file: the file needs to be mono, and nothing else. Will try to continue anyways ...");
1674 
1675  // ok, creating buffer
1676  void* bdata = malloc(dataSize);
1677  if (!bdata)
1678  {
1679  LOG("Memory error reading file "+filename);
1680  return true;
1681  }
1682  if (stream->read(bdata, dataSize) != dataSize)
1683  {
1684  LOG("Could not read file "+filename);
1685  free(bdata);
1686  return true;
1687  }
1688 
1689  //LOG("alBufferData: format "+TOSTRING(format)+" size "+TOSTRING(dataSize)+" freq "+TOSTRING(freq));
1690  alGetError(); // Reset errors
1691  ALint error;
1692  alBufferData(buffer, format, bdata, dataSize, freq);
1693  error = alGetError();
1694 
1695  free(bdata);
1696  // stream will be closed by itself
1697 
1698  if (error != AL_NO_ERROR)
1699  {
1700  LOG("OpenAL error while loading buffer for "+filename+" : "+TOSTRING(error));
1701  return true;
1702  }
1703 
1704  return false;
1705 }
1706 
1707 #endif // USE_OPENAL
RoR::SoundManager::pauseAllSounds
void pauseAllSounds()
Unlike the name suggests, this sets the listener's gain to 0, essentially muting all sounds.
Definition: SoundManager.cpp:1451
RoR::App::audio_doppler_factor
CVar * audio_doppler_factor
Definition: Application.cpp:222
Sound.h
RoR::Sound::REASON_PTCH
@ REASON_PTCH
Definition: Sound.h:74
LOGSTREAM
#define LOGSTREAM
Definition: SoundManager.cpp:34
RoR::soundsource_t::ssi
SoundScriptInstancePtr ssi
Definition: SimData.h:394
RoR::SoundManager::UpdateAlListener
void UpdateAlListener()
Updates the listener's position, orientation and velocity vectors in OpenAL.
Definition: SoundManager.cpp:534
RoR::SoundManager::loadWAVFile
bool loadWAVFile(Ogre::String filename, ALuint buffer, Ogre::String resource_group_name="")
Definition: SoundManager.cpp:1520
RoR::SoundManager::GetEfxProperties
const EFXEAXREVERBPROPERTIES * GetEfxProperties(const std::string &efx_preset_name) const
Returns a pointer to properties of an EFX preset stored in the EFX properties map.
Definition: SoundManager.cpp:313
RoR::SoundManager::assign
void assign(int source_index, int hardware_index)
Adds an audio source to hardware source.
Definition: SoundManager.cpp:1412
RoR::SoundManager::setMasterVolume
void setMasterVolume(float v)
Updates both CVar audio_master_volume and the listener's gain to the provided value.
Definition: SoundManager.cpp:1467
RoR::App::audio_enable_occlusion
CVar * audio_enable_occlusion
Definition: Application.cpp:212
RoR::SoundManager::SetListener
void SetListener(Ogre::Vector3 position, Ogre::Vector3 direction, Ogre::Vector3 up, Ogre::Vector3 velocity)
Sets position and speed of the listener.
Definition: SoundManager.cpp:492
RoR::App::audio_default_efx_preset
CVar * audio_default_efx_preset
Definition: Application.cpp:218
RoR::SoundManager::UpdateListenerEffectSlot
void UpdateListenerEffectSlot(const float dt)
Dynamically adjusts some parameters of the currently active reverb preset based on the current enviro...
Definition: SoundManager.cpp:593
RoR::App::audio_engine_controls_environmental_audio
CVar * audio_engine_controls_environmental_audio
Definition: Application.cpp:216
RoR::SoundManager::recomputeSource
void recomputeSource(int source_index, int reason, float vfl, Ogre::Vector3 *vvec)
Computes audibility of an audio source and retires it if it is inaudible.
Definition: SoundManager.cpp:1327
RoR::NODENUM_INVALID
static const NodeNum_t NODENUM_INVALID
Definition: ForwardDeclarations.h:55
RoR::Sound::pitch
float pitch
Definition: Sound.h:84
format
Truck file format(technical spec)
RoR::Sound::loop
bool loop
Definition: Sound.h:85
RoR::soundsource_t::nodenum
NodeNum_t nodenum
Definition: SimData.h:395
RoR::SoundManager::PrepopulateEfxPropertiesMap
void PrepopulateEfxPropertiesMap()
Helper function that fills the m_efx_properties_map with presets provided by OpenAL's efx-presets....
Definition: SoundManager.cpp:327
RoR::Exhaust
Definition: GfxData.h:325
SoundManager.h
RoR::CVar::getBool
bool getBool() const
Definition: CVar.h:98
RoR::SoundManager::REFERENCE_DISTANCE
static const float REFERENCE_DISTANCE
Definition: SoundManager.h:206
RoR::Sound
Definition: Sound.h:39
RoR::Sound::position
Ogre::Vector3 position
Definition: Sound.h:93
RoR::SoundManager::CreateAlEffect
ALuint CreateAlEffect(const EFXEAXREVERBPROPERTIES *efx_properties) const
Creates an OpenAL effect based on the parameters of an efx/eax reverb preset.
Definition: SoundManager.cpp:854
RoR::AeroEngineType::AE_TURBOJET
@ AE_TURBOJET
RoR::ActorManager::GetActors
ActorPtrVec & GetActors()
Definition: ActorManager.h:121
RoR::SoundManager::DeleteAlEffect
void DeleteAlEffect(const ALuint efx_effect_id) const
Deletes an OpenAL effect.
Definition: SoundManager.cpp:927
RoR::SoundScriptInstance::getTemplate
SoundScriptTemplatePtr & getTemplate()
Definition: SoundScriptManager.h:257
RoR::Sound::should_play
bool should_play
Definition: Sound.h:87
RoR::SoundManager::IsHardwareSourceObstructed
bool IsHardwareSourceObstructed(const int hardware_index) const
Performs various checks against the environment of the listener to determine whether the sound belong...
Definition: SoundManager.cpp:1012
RoR::App::audio_enable_reflection_panning
CVar * audio_enable_reflection_panning
Definition: Application.cpp:214
_checkALErrors
bool _checkALErrors(const char *filename, int linenum)
Definition: SoundManager.cpp:36
RoR::SoundManager::UpdateObstructionFilter
void UpdateObstructionFilter(const int hardware_index, const bool enable_obstruction_filter) const
Applies an obstruction filter to the provided hardware source.
Definition: SoundManager.cpp:1121
RoR::collision_box_t
Definition: SimData.h:677
RefCountingObjectPtr< Sound >
RoR::SoundManager::retire
void retire(int source_index)
Stops and the removes an audio source from hardware source.
Definition: SoundManager.cpp:1439
RoR::SoundManager::UpdateSourceFilters
void UpdateSourceFilters(const int hardware_index) const
Helper function to call several other functions to update source filters.
Definition: SoundManager.cpp:989
hasALErrors
#define hasALErrors()
Definition: SoundManager.cpp:49
RoR::SoundManager::createSound
SoundPtr createSound(Ogre::String filename, Ogre::String resource_group_name="")
Definition: SoundManager.cpp:1476
RoR::EfxReverbEngine::EAXREVERB
@ EAXREVERB
RoR::SoundManager::UpdateDirectedSounds
void UpdateDirectedSounds() const
Updates AL Cones for sources of directed sound emissions (exhausts, turboprops and turbojets).
Definition: SoundManager.cpp:1160
RoR::SoundManager::SmoothlyUpdateAlAuxiliaryEffectSlot
void SmoothlyUpdateAlAuxiliaryEffectSlot(const float dt, const ALuint slot_id, const EFXEAXREVERBPROPERTIES *target_efx_properties)
This performs a smooth update of the efx properties of an OpenAL Auxiliary Effect slot using linear i...
Definition: SoundManager.cpp:620
RoR::App::audio_master_volume
CVar * audio_master_volume
Definition: Application.cpp:209
TOSTRING
#define TOSTRING(x)
Definition: Application.h:56
RoR::SoundManager::Update
void Update(const float dt)
Does the per-frame update of sounds and listener environment.
Definition: SoundManager.cpp:403
ScrewProp.h
RoR::Collisions::intersectsTris
std::pair< bool, Ogre::Real > intersectsTris(Ogre::Ray ray)
Definition: Collisions.cpp:634
RoR::App::audio_force_listener_efx_preset
CVar * audio_force_listener_efx_preset
Definition: Application.cpp:219
RoR::NodeNum_t
uint16_t NodeNum_t
Node position within Actor::ar_nodes; use RoR::NODENUM_INVALID as empty value.
Definition: ForwardDeclarations.h:54
RoR::CVar::getStr
std::string const & getStr() const
Definition: CVar.h:95
RoR::SoundManager::resumeAllSounds
void resumeAllSounds()
Unlike the name suggests, this sets the listener's gain to the value of the CVar audio_master_volume.
Definition: SoundManager.cpp:1459
RoR::Sound::REASON_LOOP
@ REASON_LOOP
Definition: Sound.h:73
RoR::ActorPtrVec
std::vector< ActorPtr > ActorPtrVec
Definition: ForwardDeclarations.h:246
RoR::App::app_state
CVar * app_state
Definition: Application.cpp:79
RoR::Sound::REASON_PLAY
@ REASON_PLAY
Definition: Sound.h:70
RoR::EfxReverbEngine::NONE
@ NONE
RoR::Terrain::GetCollisions
Collisions * GetCollisions()
Definition: Terrain.h:85
RoR::App::audio_sim_pause_disables_doppler_effect
CVar * audio_sim_pause_disables_doppler_effect
Definition: Application.cpp:224
RoR::SoundManager::UpdateListenerEnvironment
void UpdateListenerEnvironment()
Determines several properties of the environment of the listener and updates OpenAL to use them.
Definition: SoundManager.cpp:505
RoR::SoundManager::CleanUp
void CleanUp()
Cleans up various objects that should be reset when returning from a terrain to the main menu.
Definition: SoundManager.cpp:293
RoR::SoundManager::UpdateGlobalDopplerFactor
void UpdateGlobalDopplerFactor() const
Updates the global Doppler factor based on CVar settings and the state of the physics simulation.
Definition: SoundManager.cpp:418
RoR::SoundScriptTemplate::getNumSounds
int getNumSounds()
Definition: SoundScriptManager.h:187
Application.h
Central state/object manager and communications hub.
RoR::SoundManager::UpdateConeProperties
void UpdateConeProperties(const ALuint source, const Ogre::Vector3 &cone_direction, const float cone_inner_angle, const float cone_outer_angle, const float cone_outer_gain, const float cone_outer_gain_hf) const
Updates the Cone properties for the hardware source.
Definition: SoundManager.cpp:1306
RoR::App::GetGameContext
GameContext * GetGameContext()
Definition: Application.cpp:296
RoR::SoundManager::recomputeAllSources
void recomputeAllSources()
Definition: SoundManager.cpp:947
RoR::EfxReverbEngine::REVERB
@ REVERB
RoR::App::audio_enable_directed_sounds
CVar * audio_enable_directed_sounds
Definition: Application.cpp:213
RoR::Sound::REASON_STOP
@ REASON_STOP
Definition: Sound.h:71
RoR::AIRPLANE
@ AIRPLANE
its an airplane
Definition: SimData.h:86
RoR::SoundManager::UpdateEfxSpecificProperties
void UpdateEfxSpecificProperties(const float dt)
Updates properties of OpenAL facilities that are only available with EFX.
Definition: SoundManager.cpp:436
RoR::AppState::SIMULATION
@ SIMULATION
RoR::App::audio_enable_efx
CVar * audio_enable_efx
Definition: Application.cpp:215
RoR::SoundManager::SoundManager
SoundManager()
Definition: SoundManager.cpp:58
RoR::SoundManager::MAX_DISTANCE
static const float MAX_DISTANCE
Definition: SoundManager.h:204
RoR::App::audio_force_obstruction_inside_vehicles
CVar * audio_force_obstruction_inside_vehicles
Definition: Application.cpp:220
IWater.h
RoR::App::audio_efx_reverb_engine
CVar * audio_efx_reverb_engine
Definition: Application.cpp:217
compareByAudibility
bool compareByAudibility(std::pair< int, float > a, std::pair< int, float > b)
Definition: SoundManager.cpp:941
RoR::CVar::setVal
void setVal(T val)
Definition: CVar.h:72
RoR::SoundManager::GetReverbPresetAt
const EFXEAXREVERBPROPERTIES * GetReverbPresetAt(Ogre::Vector3 position) const
Determines which reverb preset corresponds to the provided position and returns its properties.
Definition: SoundManager.cpp:551
RoR::SoundManager::UpdateOcclusionFilter
bool UpdateOcclusionFilter(const int hardware_index, const ALuint effect_slot_id, const EFXEAXREVERBPROPERTIES *reference_efx_reverb_properties) const
Applies an occlusion filter to the provided source if certain conditions apply.
Definition: SoundManager.cpp:1134
RoR::Sound::buffer
ALuint buffer
Definition: Sound.h:91
RoR::AeroEngineType::AE_XPROP
@ AE_XPROP
RoR::Sound::getPosition
Ogre::Vector3 getPosition()
Definition: Sound.h:64
AeroEngine.h
RoR::CVar::getFloat
float getFloat() const
Definition: CVar.h:96
Ogre
Definition: ExtinguishableFireAffector.cpp:35
RoR::SoundScriptInstance::getSound
const SoundPtr & getSound(int pos)
Definition: SoundScriptManager.h:260
RoR::BOAT
@ BOAT
its a boat
Definition: SimData.h:87
RoR::Sound::REASON_POSN
@ REASON_POSN
Definition: Sound.h:75
RoR::SoundManager::SetDopplerFactor
void SetDopplerFactor(const float doppler_factor) const
Updates the global Doppler factor in OpenAL with the provided value.
Definition: SoundManager.h:155
RoR::App::audio_enable_obstruction
CVar * audio_enable_obstruction
Definition: Application.cpp:211
RoR::Sound::REASON_GAIN
@ REASON_GAIN
Definition: Sound.h:72
RoR::App::audio_device_name
CVar * audio_device_name
Definition: Application.cpp:221
RoR::SoundManager::~SoundManager
~SoundManager()
Definition: SoundManager.cpp:253
RoR::Sound::REASON_VLCT
@ REASON_VLCT
Definition: Sound.h:76
RoR
Definition: AppContext.h:36
RoR::soundsource_t
Definition: SimData.h:389
RoR::Collisions::intersectsTerrain
std::pair< bool, Ogre::Real > intersectsTerrain(Ogre::Ray ray)
Checks whether a Ray intersects the terrain.
Definition: Collisions.cpp:677
RoR::SoundManager::ComputeEarlyReflectionsProperties
std::tuple< Ogre::Vector3, float, float > ComputeEarlyReflectionsProperties() const
Detects surfaces close to the listener and calculates a user-relative (as opposed to listener-relativ...
Definition: SoundManager.cpp:738
RoR::GameContext::GetActorManager
ActorManager * GetActorManager()
Definition: GameContext.h:127
RoR::CVar::setStr
void setStr(std::string const &str)
Definition: CVar.h:83
RoR::Sound::velocity
Ogre::Vector3 velocity
Definition: Sound.h:94
RoR::Terrain::getWater
IWater * getWater()
Definition: Terrain.h:86
RoR::Sound::gain
float gain
Definition: Sound.h:83
RoR::GameContext::GetTerrain
const TerrainPtr & GetTerrain()
Definition: GameContext.h:117
RoR::SoundManager::ROLLOFF_FACTOR
static const float ROLLOFF_FACTOR
Definition: SoundManager.h:205
RoR::SoundManager::UpdateSourceSpecificDopplerFactor
void UpdateSourceSpecificDopplerFactor(const int hardware_index) const
Identifies the actor to which the sound corresponding to a hardware source belongs and updates the Do...
Definition: SoundManager.cpp:458