Skip to content

Commit

Permalink
✨ Add preliminary support for reverb using OpenAL EFX
Browse files Browse the repository at this point in the history
  • Loading branch information
Hiradur committed Sep 25, 2024
1 parent c010e3c commit e061fe0
Show file tree
Hide file tree
Showing 8 changed files with 264 additions and 0 deletions.
2 changes: 2 additions & 0 deletions source/main/Application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,8 @@ CVar* io_invert_orbitcam;
// Audio
CVar* audio_master_volume;
CVar* audio_enable_creak;
CVar* audio_enable_efx;
CVar* audio_force_efx_preset;
CVar* audio_device_name;
CVar* audio_doppler_factor;
CVar* audio_menu_music;
Expand Down
2 changes: 2 additions & 0 deletions source/main/Application.h
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,8 @@ extern CVar* io_invert_orbitcam;
// Audio
extern CVar* audio_master_volume;
extern CVar* audio_enable_creak;
extern CVar* audio_enable_efx;
extern CVar* audio_force_efx_preset;
extern CVar* audio_device_name;
extern CVar* audio_doppler_factor;
extern CVar* audio_menu_music;
Expand Down
192 changes: 192 additions & 0 deletions source/main/audio/SoundManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,57 @@ SoundManager::SoundManager()
if (alcGetString(audio_device, ALC_DEVICE_SPECIFIER)) LOG("SoundManager: OpenAL device is: " + String(alcGetString(audio_device, ALC_DEVICE_SPECIFIER)));
if (alcGetString(audio_device, ALC_EXTENSIONS)) LOG("SoundManager: OpenAL ALC extensions are: " + String(alcGetString(audio_device, ALC_EXTENSIONS)));

// initialize use of OpenAL EFX extensions
this->efx_is_available = alcIsExtensionPresent(audio_device, "ALC_EXT_EFX");
if (efx_is_available)
{
LOG("SoundManager: Found OpenAL EFX extension");

// Get OpenAL function pointers
alGenEffects = (LPALGENEFFECTS)alGetProcAddress("alGenEffects");
alDeleteEffects = (LPALDELETEEFFECTS)alGetProcAddress("alDeleteEffects");
alIsEffect = (LPALISEFFECT)alGetProcAddress("alIsEffect");
alEffecti = (LPALEFFECTI)alGetProcAddress("alEffecti");
alEffectf = (LPALEFFECTF)alGetProcAddress("alEffectf");
alEffectfv = (LPALEFFECTFV)alGetProcAddress("alEffectfv");
alGenFilters = (LPALGENFILTERS)alGetProcAddress("alGenFilters");
alDeleteFilters = (LPALDELETEFILTERS)alGetProcAddress("alDeleteFilters");
alIsFilter = (LPALISFILTER)alGetProcAddress("alIsFilter");
alFilteri = (LPALFILTERI)alGetProcAddress("alFilteri");
alFilterf = (LPALFILTERF)alGetProcAddress("alFilterf");
alGenAuxiliaryEffectSlots = (LPALGENAUXILIARYEFFECTSLOTS)alGetProcAddress("alGenAuxiliaryEffectSlots");
alDeleteAuxiliaryEffectSlots = (LPALDELETEAUXILIARYEFFECTSLOTS)alGetProcAddress("alDeleteAuxiliaryEffectSlots");
alIsAuxiliaryEffectSlot = (LPALISAUXILIARYEFFECTSLOT)alGetProcAddress("alIsAuxiliaryEffectSlot");
alAuxiliaryEffectSloti = (LPALAUXILIARYEFFECTSLOTI)alGetProcAddress("alAuxiliaryEffectSloti");
alAuxiliaryEffectSlotf = (LPALAUXILIARYEFFECTSLOTF)alGetProcAddress("alAuxiliaryEffectSlotf");
alAuxiliaryEffectSlotfv = (LPALAUXILIARYEFFECTSLOTFV)alGetProcAddress("alAuxiliaryEffectSlotfv");

if (App::audio_enable_efx->getBool())
{
// create effect slot for the listener
if(!this->alIsAuxiliaryEffectSlot(listener_slot))
{
alGetError();

this->alGenAuxiliaryEffectSlots(1, &listener_slot);
ALuint e = alGetError();

if (e != AL_NO_ERROR)
{
LOG("SoundManager: alGenAuxiliaryEffectSlots for listener_slot failed: " + e);
listener_slot = AL_EFFECTSLOT_NULL;
}
}

this->build_efx_property_map();
}
}
else
{
LOG("SoundManager: OpenAL EFX extension not found, disabling EFX");
App::audio_enable_efx->setVal(false);
}

// generate the AL sources
for (hardware_sources_num = 0; hardware_sources_num < MAX_HARDWARE_SOURCES; hardware_sources_num++)
{
Expand All @@ -106,6 +157,12 @@ SoundManager::SoundManager()
alSourcef(hardware_sources[hardware_sources_num], AL_REFERENCE_DISTANCE, REFERENCE_DISTANCE);
alSourcef(hardware_sources[hardware_sources_num], AL_ROLLOFF_FACTOR, ROLLOFF_FACTOR);
alSourcef(hardware_sources[hardware_sources_num], AL_MAX_DISTANCE, MAX_DISTANCE);

// connect source to listener slot effect
if(App::audio_enable_efx->getBool())
{
alSource3i(hardware_sources[hardware_sources_num], AL_AUXILIARY_SEND_FILTER, listener_slot, 0, AL_FILTER_NULL);
}
}

alDopplerFactor(App::audio_doppler_factor->getFloat());
Expand All @@ -115,6 +172,8 @@ SoundManager::SoundManager()
{
hardware_sources_map[i] = -1;
}


}

SoundManager::~SoundManager()
Expand All @@ -123,6 +182,18 @@ SoundManager::~SoundManager()
alDeleteSources(MAX_HARDWARE_SOURCES, hardware_sources);
alDeleteBuffers(MAX_AUDIO_BUFFERS, audio_buffers);

if(efx_is_available)
{
// TODO: alDeleteEffects

if (alIsAuxiliaryEffectSlot(listener_slot))
{
alAuxiliaryEffectSloti(listener_slot, AL_EFFECTSLOT_EFFECT, AL_EFFECTSLOT_NULL);
alDeleteAuxiliaryEffectSlots(1, &listener_slot);
listener_slot = AL_EFFECTSLOT_NULL;
}
}

// destroy the sound context and device
sound_context = alcGetCurrentContext();
audio_device = alcGetContextsDevice(sound_context);
Expand All @@ -135,6 +206,49 @@ SoundManager::~SoundManager()
LOG("SoundManager destroyed.");
}

void SoundManager::build_efx_property_map()
{
this->efx_properties_map["EFX_REVERB_PRESET_GENERIC"] = EFX_REVERB_PRESET_GENERIC;
this->efx_properties_map["EFX_REVERB_PRESET_CAVE"] = EFX_REVERB_PRESET_CAVE;
this->efx_properties_map["EFX_REVERB_PRESET_ARENA"] = EFX_REVERB_PRESET_ARENA;
this->efx_properties_map["EFX_REVERB_PRESET_HANGAR"] = EFX_REVERB_PRESET_HANGAR;
this->efx_properties_map["EFX_REVERB_PRESET_ALLEY"] = EFX_REVERB_PRESET_ALLEY;
this->efx_properties_map["EFX_REVERB_PRESET_FOREST"] = EFX_REVERB_PRESET_FOREST;
this->efx_properties_map["EFX_REVERB_PRESET_CITY"] = EFX_REVERB_PRESET_CITY;
this->efx_properties_map["EFX_REVERB_PRESET_MOUNTAINS"] = EFX_REVERB_PRESET_MOUNTAINS;
this->efx_properties_map["EFX_REVERB_PRESET_QUARRY"] = EFX_REVERB_PRESET_QUARRY;
this->efx_properties_map["EFX_REVERB_PRESET_PLAIN"] = EFX_REVERB_PRESET_PLAIN;
this->efx_properties_map["EFX_REVERB_PRESET_PARKINGLOT"] = EFX_REVERB_PRESET_PARKINGLOT;
this->efx_properties_map["EFX_REVERB_PRESET_UNDERWATER"] = EFX_REVERB_PRESET_UNDERWATER;
this->efx_properties_map["EFX_REVERB_PRESET_DRUGGED"] = EFX_REVERB_PRESET_DRUGGED;
this->efx_properties_map["EFX_REVERB_PRESET_DIZZY"] = EFX_REVERB_PRESET_DIZZY;
this->efx_properties_map["EFX_REVERB_PRESET_CASTLE_COURTYARD"] = EFX_REVERB_PRESET_CASTLE_COURTYARD;
this->efx_properties_map["EFX_REVERB_PRESET_FACTORY_HALL"] = EFX_REVERB_PRESET_FACTORY_HALL;
this->efx_properties_map["EFX_REVERB_PRESET_SPORT_EMPTYSTADIUM"] = EFX_REVERB_PRESET_SPORT_EMPTYSTADIUM;
this->efx_properties_map["EFX_REVERB_PRESET_SPORT_EMPTYSTADIUM"] = EFX_REVERB_PRESET_SPORT_EMPTYSTADIUM;
this->efx_properties_map["EFX_REVERB_PRESET_PIPE_LARGE"] = EFX_REVERB_PRESET_PIPE_LARGE;
this->efx_properties_map["EFX_REVERB_PRESET_PIPE_LONGTHIN"] = EFX_REVERB_PRESET_PIPE_LONGTHIN;
this->efx_properties_map["EFX_REVERB_PRESET_PIPE_RESONANT"] = EFX_REVERB_PRESET_PIPE_RESONANT;
this->efx_properties_map["EFX_REVERB_PRESET_OUTDOORS_BACKYARD"] = EFX_REVERB_PRESET_OUTDOORS_BACKYARD;
this->efx_properties_map["EFX_REVERB_PRESET_OUTDOORS_ROLLINGPLAINS"] = EFX_REVERB_PRESET_OUTDOORS_ROLLINGPLAINS;
this->efx_properties_map["EFX_REVERB_PRESET_OUTDOORS_DEEPCANYON"] = EFX_REVERB_PRESET_OUTDOORS_DEEPCANYON;
this->efx_properties_map["EFX_REVERB_PRESET_OUTDOORS_CREEK"] = EFX_REVERB_PRESET_OUTDOORS_CREEK;
this->efx_properties_map["EFX_REVERB_PRESET_OUTDOORS_VALLEY"] = EFX_REVERB_PRESET_OUTDOORS_VALLEY;
this->efx_properties_map["EFX_REVERB_PRESET_MOOD_HEAVEN"] = EFX_REVERB_PRESET_MOOD_HEAVEN;
this->efx_properties_map["EFX_REVERB_PRESET_MOOD_HELL"] = EFX_REVERB_PRESET_MOOD_HELL;
this->efx_properties_map["EFX_REVERB_PRESET_MOOD_MEMORY"] = EFX_REVERB_PRESET_MOOD_MEMORY;
this->efx_properties_map["EFX_REVERB_PRESET_DRIVING_COMMENTATOR"] = EFX_REVERB_PRESET_DRIVING_COMMENTATOR;
this->efx_properties_map["EFX_REVERB_PRESET_DRIVING_PITGARAGE"] = EFX_REVERB_PRESET_DRIVING_PITGARAGE;
this->efx_properties_map["EFX_REVERB_PRESET_DRIVING_INCAR_RACER"] = EFX_REVERB_PRESET_DRIVING_INCAR_RACER;
this->efx_properties_map["EFX_REVERB_PRESET_DRIVING_INCAR_SPORTS"] = EFX_REVERB_PRESET_DRIVING_INCAR_SPORTS;
this->efx_properties_map["EFX_REVERB_PRESET_DRIVING_INCAR_LUXURY"] = EFX_REVERB_PRESET_DRIVING_INCAR_LUXURY;
this->efx_properties_map["EFX_REVERB_PRESET_DRIVING_TUNNEL"] = EFX_REVERB_PRESET_DRIVING_TUNNEL;
this->efx_properties_map["EFX_REVERB_PRESET_CITY_STREETS"] = EFX_REVERB_PRESET_CITY_STREETS;
this->efx_properties_map["EFX_REVERB_PRESET_CITY_SUBWAY"] = EFX_REVERB_PRESET_CITY_SUBWAY;
this->efx_properties_map["EFX_REVERB_PRESET_CITY_UNDERPASS"] = EFX_REVERB_PRESET_CITY_UNDERPASS;
this->efx_properties_map["EFX_REVERB_PRESET_CITY_ABANDONED"] = EFX_REVERB_PRESET_CITY_ABANDONED;
}

void SoundManager::setListener(Ogre::Vector3 position, Ogre::Vector3 direction, Ogre::Vector3 up, Ogre::Vector3 velocity)
{
if (!audio_device)
Expand All @@ -155,6 +269,84 @@ void SoundManager::setListener(Ogre::Vector3 position, Ogre::Vector3 direction,
alListener3f(AL_POSITION, position.x, position.y, position.z);
alListener3f(AL_VELOCITY, velocity.x, velocity.y, velocity.z);
alListenerfv(AL_ORIENTATION, orientation);

if(App::audio_enable_efx->getBool())
{
this->updateListenerEnvironment();
}
}

void SoundManager::setListenerEnvironment(std::string listener_efx_preset_name)
{
if(efx_properties_map.find(listener_efx_preset_name) == efx_properties_map.end())
{
// LOG("SoundManager: EFX preset `" + listener_efx_preset_name + "` is not available");
listener_efx_preset_name = ""; // force that no preset is active
}

if(listener_efx_preset_name != this->listener_efx_preset_name)
{
this->listener_efx_preset_name = listener_efx_preset_name;
listener_efx_environment_has_changed = true;
}
else
{
listener_efx_environment_has_changed = false;
}
}

void SoundManager::updateListenerEnvironment()
{
if (listener_efx_environment_has_changed)
{
if (listener_efx_preset_name.empty())
{
alAuxiliaryEffectSloti(listener_slot, AL_EFFECTSLOT_EFFECT, AL_EFFECTSLOT_NULL);
}
else
{
// TODO: Reuse already existing effects
ALuint effect = this->CreateAlEffect(&this->efx_properties_map[listener_efx_preset_name]);
alAuxiliaryEffectSloti(listener_slot, AL_EFFECTSLOT_EFFECT, effect);
}
}
}

ALuint SoundManager::CreateAlEffect(const EFXEAXREVERBPROPERTIES* efx_properties)
{
ALuint effect = 0;
ALenum error;

alGenEffects(1, &effect);
{
alEffecti(effect, AL_EFFECT_TYPE, AL_EFFECT_REVERB);

alEffectf(effect, AL_REVERB_DENSITY, efx_properties->flDensity);
alEffectf(effect, AL_REVERB_DIFFUSION, efx_properties->flDiffusion);
alEffectf(effect, AL_REVERB_GAIN, efx_properties->flGain);
alEffectf(effect, AL_REVERB_GAINHF, efx_properties->flGainHF);
alEffectf(effect, AL_REVERB_DECAY_TIME, efx_properties->flDecayTime);
alEffectf(effect, AL_REVERB_DECAY_HFRATIO, efx_properties->flDecayHFRatio);
alEffectf(effect, AL_REVERB_REFLECTIONS_GAIN, efx_properties->flReflectionsGain);
alEffectf(effect, AL_REVERB_REFLECTIONS_DELAY, efx_properties->flReflectionsDelay);
alEffectf(effect, AL_REVERB_LATE_REVERB_GAIN, efx_properties->flLateReverbGain);
alEffectf(effect, AL_REVERB_LATE_REVERB_DELAY, efx_properties->flLateReverbDelay);
alEffectf(effect, AL_REVERB_AIR_ABSORPTION_GAINHF, efx_properties->flAirAbsorptionGainHF);
alEffectf(effect, AL_REVERB_ROOM_ROLLOFF_FACTOR, efx_properties->flRoomRolloffFactor);
alEffecti(effect, AL_REVERB_DECAY_HFLIMIT, efx_properties->iDecayHFLimit);
}
/* Check if an error occured, and clean up if so. */
error = alGetError();
if(error != AL_NO_ERROR)
{
LOG("ERROR in SoundManager::LoadEffect: Could not create EFX effect:" + error);

if(alIsEffect(effect))
alDeleteEffects(1, &effect);
return 0;
}

return effect;
}

bool compareByAudibility(std::pair<int, float> a, std::pair<int, float> b)
Expand Down
34 changes: 34 additions & 0 deletions source/main/audio/SoundManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,13 @@
#ifdef __APPLE__
#include <OpenAL/al.h>
#include <OpenAL/alc.h>
#include <OpenAL/alext.h>
#include <OpenAL/efx-presets.h>
#else
#include <AL/al.h>
#include <AL/alc.h>
#include <AL/alext.h>
#include <AL/efx-presets.h>
#endif // __APPLE__

namespace RoR {
Expand All @@ -56,6 +60,8 @@ class SoundManager
SoundPtr createSound(Ogre::String filename, Ogre::String resource_group_name = "");

void setListener(Ogre::Vector3 position, Ogre::Vector3 direction, Ogre::Vector3 up, Ogre::Vector3 velocity);
void setListenerEnvironment(std::string listener_environment);
void updateListenerEnvironment();
void pauseAllSounds();
void resumeAllSounds();
void setMasterVolume(float v);
Expand All @@ -66,6 +72,7 @@ class SoundManager
void setSpeedOfSound(float speed_of_sound) { alSpeedOfSound(speed_of_sound); }
float getDopplerFactor() { return alGetFloat(AL_DOPPLER_FACTOR); }
void setDopplerFactor(float doppler_factor) { alDopplerFactor(doppler_factor); }
std::string getReverbPresetAt(Ogre::Vector3 position);

int getNumHardwareSources() { return hardware_sources_num; }

Expand Down Expand Up @@ -104,6 +111,33 @@ class SoundManager
Ogre::Vector3 listener_position = Ogre::Vector3::ZERO;
ALCdevice* audio_device = nullptr;
ALCcontext* sound_context = nullptr;

// OpenAL EFX stuff
bool efx_is_available = false;
bool listener_efx_environment_has_changed = true;
ALuint listener_slot = 0;
std::string listener_efx_preset_name;
std::map<std::string, EFXEAXREVERBPROPERTIES> efx_properties_map;
LPALGENEFFECTS alGenEffects = nullptr;
LPALDELETEEFFECTS alDeleteEffects = nullptr;
LPALISEFFECT alIsEffect = nullptr;
LPALEFFECTI alEffecti = nullptr;
LPALEFFECTF alEffectf = nullptr;
LPALEFFECTFV alEffectfv = nullptr;
LPALGENFILTERS alGenFilters = nullptr;
LPALDELETEFILTERS alDeleteFilters = nullptr;
LPALISFILTER alIsFilter = nullptr;
LPALFILTERI alFilteri = nullptr;
LPALFILTERF alFilterf = nullptr;
LPALGENAUXILIARYEFFECTSLOTS alGenAuxiliaryEffectSlots = nullptr;
LPALDELETEAUXILIARYEFFECTSLOTS alDeleteAuxiliaryEffectSlots = nullptr;
LPALISAUXILIARYEFFECTSLOT alIsAuxiliaryEffectSlot = nullptr;
LPALAUXILIARYEFFECTSLOTI alAuxiliaryEffectSloti = nullptr;
LPALAUXILIARYEFFECTSLOTF alAuxiliaryEffectSlotf = nullptr;
LPALAUXILIARYEFFECTSLOTFV alAuxiliaryEffectSlotfv = nullptr;

ALuint CreateAlEffect(const EFXEAXREVERBPROPERTIES* efx_properties);
void build_efx_property_map();
};

/// @}
Expand Down
30 changes: 30 additions & 0 deletions source/main/audio/SoundScriptManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,36 @@ void SoundScriptManager::setListenerEnvironment(Vector3 listener_position)
{
sound_manager->setSpeedOfSound(343.3f); // assume listener is in air at 20° celsius
}

if (App::audio_enable_efx->getBool())
{
std::string listener_environment;

if(!App::audio_force_efx_preset->getStr().empty())
{
listener_environment = App::audio_force_efx_preset->getStr();
}
else
{
if(listener_is_underwater)
{
listener_environment = "EFX_REVERB_PRESET_UNDERWATER";
}
else
{
listener_environment = this->getReverbPresetAt(listener_position);
}
// TODO: Might want to set an in-cockpit effect when appropriate
}

sound_manager->setListenerEnvironment(listener_environment);
}
}

std::string SoundScriptManager::getReverbPresetAt(Ogre::Vector3 position)
{
// TODO: This is a stub
return "";
}

void SoundScriptManager::setDopplerFactor(float doppler_factor)
Expand Down
1 change: 1 addition & 0 deletions source/main/audio/SoundScriptManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,7 @@ class SoundScriptManager : public Ogre::ScriptLoader
// soundLinks, soundItems, actor_ids, triggers
std::map <int, std::map <int, std::map <int, std::map <int, bool > > > > state_map;

std::string getReverbPresetAt(Ogre::Vector3 position);
void setListenerEnvironment(Ogre::Vector3 position);

SoundManager* sound_manager;
Expand Down
1 change: 1 addition & 0 deletions source/main/gui/panels/GUI_GameSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,7 @@ void GameSettings::DrawAudioSettings()
}

DrawGCheckbox(App::audio_enable_creak, _LC("GameSettings", "Creak sound"));
DrawGCheckbox(App::audio_enable_efx, _LC("GameSettings", "Enable advanced sound effects via OpenAL EFX"));
DrawGCheckbox(App::audio_menu_music, _LC("GameSettings", "Main menu music"));
DrawGFloatSlider(App::audio_master_volume, _LC("GameSettings", "Master volume"), 0, 1);
DrawGFloatSlider(App::audio_doppler_factor, _LC("GameSettings", "Doppler factor (requires restart)"), 0, 10);
Expand Down
2 changes: 2 additions & 0 deletions source/main/system/CVar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ void Console::cVarSetupBuiltins()
App::audio_device_name = this->cVarCreate("audio_device_name", "AudioDevice", CVAR_ARCHIVE);
App::audio_doppler_factor = this->cVarCreate("audio_doppler_factor", "Doppler Factor", CVAR_ARCHIVE | CVAR_TYPE_FLOAT, "1.0");
App::audio_menu_music = this->cVarCreate("audio_menu_music", "MainMenuMusic", CVAR_ARCHIVE | CVAR_TYPE_BOOL, "false");
App::audio_enable_efx = this->cVarCreate("audio_enable_efx", "Enable OpenAL EFX", CVAR_ARCHIVE | CVAR_TYPE_BOOL, "true");
App::audio_force_efx_preset = this->cVarCreate("audio_force_efx_preset", "Enforce OpenAL EFX preset", 0, "");

App::gfx_flares_mode = this->cVarCreate("gfx_flares_mode", "Lights", CVAR_ARCHIVE | CVAR_TYPE_INT, "4"/*(int)GfxFlaresMode::ALL_VEHICLES_ALL_LIGHTS*/);
App::gfx_polygon_mode = this->cVarCreate("gfx_polygon_mode", "Polygon mode", CVAR_TYPE_INT, "1"/*(int)Ogre::PM_SOLID*/);
Expand Down

0 comments on commit e061fe0

Please sign in to comment.