| [3060] | 1 | /* | 
|---|
 | 2 |  *   ORXONOX - the hottest 3D action shooter ever to exist | 
|---|
 | 3 |  *                    > www.orxonox.net < | 
|---|
 | 4 |  * | 
|---|
 | 5 |  * | 
|---|
 | 6 |  *   License notice: | 
|---|
 | 7 |  * | 
|---|
 | 8 |  *   This program is free software; you can redistribute it and/or | 
|---|
 | 9 |  *   modify it under the terms of the GNU General Public License | 
|---|
 | 10 |  *   as published by the Free Software Foundation; either version 2 | 
|---|
 | 11 |  *   of the License, or (at your option) any later version. | 
|---|
 | 12 |  * | 
|---|
 | 13 |  *   This program 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 this program; if not, write to the Free Software | 
|---|
 | 20 |  *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. | 
|---|
 | 21 |  * | 
|---|
 | 22 |  *   Author: | 
|---|
 | 23 |  *       Erwin 'vaiursch' Herrsche | 
|---|
| [6069] | 24 |  *       Kevin Young | 
|---|
| [3060] | 25 |  *   Co-authors: | 
|---|
 | 26 |  *      ... | 
|---|
 | 27 |  * | 
|---|
 | 28 |  */ | 
|---|
 | 29 |  | 
|---|
| [3196] | 30 | #include "SoundManager.h" | 
|---|
 | 31 |  | 
|---|
| [3060] | 32 | #include <AL/alut.h> | 
|---|
| [6069] | 33 | #include <utility> | 
|---|
| [3060] | 34 |  | 
|---|
| [5929] | 35 | #include "util/Exception.h" | 
|---|
| [3196] | 36 | #include "util/Math.h" | 
|---|
| [5929] | 37 | #include "util/ScopeGuard.h" | 
|---|
| [6031] | 38 | #include "util/StringUtils.h" | 
|---|
| [6046] | 39 | #include "util/Clock.h" | 
|---|
| [5929] | 40 | #include "core/GameMode.h" | 
|---|
 | 41 | #include "core/ScopedSingletonManager.h" | 
|---|
| [6046] | 42 | #include "core/ConfigValueIncludes.h" | 
|---|
| [5982] | 43 | #include "BaseSound.h" | 
|---|
| [6046] | 44 | #include "AmbientSound.h" | 
|---|
| [3060] | 45 |  | 
|---|
 | 46 | namespace orxonox | 
|---|
 | 47 | { | 
|---|
| [3370] | 48 |     SoundManager* SoundManager::singletonPtr_s = NULL; | 
|---|
| [5929] | 49 |     ManageScopedSingleton(SoundManager, ScopeID::Graphics, true); | 
|---|
| [3060] | 50 |  | 
|---|
 | 51 |     SoundManager::SoundManager() | 
|---|
 | 52 |     { | 
|---|
| [6046] | 53 |         RegisterRootObject(SoundManager); | 
|---|
 | 54 |  | 
|---|
| [6069] | 55 |         if (!alutInitWithoutContext(NULL, NULL)) | 
|---|
| [5946] | 56 |             ThrowException(InitialisationFailed, "Sound: OpenAL ALUT error: " << alutGetErrorString(alutGetError())); | 
|---|
| [5929] | 57 |         Loki::ScopeGuard alutExitGuard = Loki::MakeGuard(&alutExit); | 
|---|
 | 58 |  | 
|---|
| [5946] | 59 |         COUT(3) << "Sound: OpenAL: Opening sound device..." << std::endl; | 
|---|
| [5929] | 60 |         this->device_ = alcOpenDevice(NULL); | 
|---|
 | 61 |         if (this->device_ == NULL) | 
|---|
| [3060] | 62 |         { | 
|---|
| [5946] | 63 |             COUT(0) << "Sound: OpenaAL: Could not open sound device. Have you installed OpenAL?" << std::endl; | 
|---|
| [5929] | 64 | #ifdef ORXONOX_PLATFORM_WINDOWS | 
|---|
| [5946] | 65 |             COUT(0) << "Sound: Just getting the DLL with the dependencies is not enough for Windows (esp. Windows 7)!" << std::endl; | 
|---|
| [5929] | 66 | #endif | 
|---|
| [5946] | 67 |             ThrowException(InitialisationFailed, "Sound: OpenAL error: Could not open sound device."); | 
|---|
| [3060] | 68 |         } | 
|---|
| [5929] | 69 |         Loki::ScopeGuard closeDeviceGuard = Loki::MakeGuard(&alcCloseDevice, this->device_); | 
|---|
 | 70 |  | 
|---|
| [5946] | 71 |         COUT(3) << "Sound: OpenAL: Sound device opened" << std::endl; | 
|---|
| [5929] | 72 |         this->context_ = alcCreateContext(this->device_, NULL); | 
|---|
 | 73 |         if (this->context_ == NULL) | 
|---|
| [5946] | 74 |             ThrowException(InitialisationFailed, "Sound: OpenAL error: Could not create sound context"); | 
|---|
| [5929] | 75 |         Loki::ScopeGuard desroyContextGuard = Loki::MakeGuard(&alcDestroyContext, this->context_); | 
|---|
 | 76 |  | 
|---|
 | 77 |         if (alcMakeContextCurrent(this->context_) == AL_TRUE) | 
|---|
| [5946] | 78 |             COUT(3) << "Sound: OpenAL: Context " << this->context_ << " loaded" << std::endl; | 
|---|
| [5929] | 79 |  | 
|---|
 | 80 |         COUT(4) << "Sound: OpenAL ALUT version: " << alutGetMajorVersion() << "." << alutGetMinorVersion() << std::endl; | 
|---|
 | 81 |  | 
|---|
 | 82 |         const char* str = alutGetMIMETypes(ALUT_LOADER_BUFFER); | 
|---|
 | 83 |         if (str == NULL) | 
|---|
| [5946] | 84 |             COUT(2) << "Sound: OpenAL ALUT error: " << alutGetErrorString(alutGetError()) << std::endl; | 
|---|
| [3060] | 85 |         else | 
|---|
| [5946] | 86 |             COUT(4) << "Sound: OpenAL ALUT supported MIME types: " << str << std::endl; | 
|---|
| [3060] | 87 |  | 
|---|
| [5929] | 88 |         GameMode::setPlaysSound(true); | 
|---|
 | 89 |         // Disarm guards | 
|---|
 | 90 |         alutExitGuard.Dismiss(); | 
|---|
 | 91 |         closeDeviceGuard.Dismiss(); | 
|---|
 | 92 |         desroyContextGuard.Dismiss(); | 
|---|
| [6046] | 93 |  | 
|---|
 | 94 |         this->setConfigValues(); | 
|---|
| [3060] | 95 |     } | 
|---|
 | 96 |  | 
|---|
 | 97 |     SoundManager::~SoundManager() | 
|---|
 | 98 |     { | 
|---|
| [5929] | 99 |         GameMode::setPlaysSound(false); | 
|---|
| [3060] | 100 |         alcDestroyContext(this->context_); | 
|---|
| [3280] | 101 |         alcCloseDevice(this->device_); | 
|---|
| [3060] | 102 |         alutExit(); | 
|---|
 | 103 |     } | 
|---|
 | 104 |  | 
|---|
| [6069] | 105 |     void SoundManager::update(const Clock& time) | 
|---|
| [6046] | 106 |     { | 
|---|
| [6069] | 107 |         this->processCrossFading(time.getDeltaTime()); | 
|---|
| [6046] | 108 |     } | 
|---|
 | 109 |  | 
|---|
 | 110 |     void SoundManager::setConfigValues() | 
|---|
 | 111 |     { | 
|---|
| [6069] | 112 |         SetConfigValue(crossFadeStep_, 0.2f) | 
|---|
| [6046] | 113 |             .description("Determines how fast sounds should fade, per second.") | 
|---|
 | 114 |             .callback(this, &SoundManager::checkFadeStepValidity); | 
|---|
 | 115 |     } | 
|---|
 | 116 |  | 
|---|
| [6069] | 117 |     void SoundManager::checkFadeStepValidity() | 
|---|
 | 118 |     { | 
|---|
 | 119 |         if (crossFadeStep_ <= 0.0 || crossFadeStep_ >= 1.0 ) | 
|---|
 | 120 |         { | 
|---|
 | 121 |             COUT(2) << "Sound warning: Sound step out of range, ignoring change." << std::endl; | 
|---|
 | 122 |             ResetConfigValue(crossFadeStep_); | 
|---|
 | 123 |         } | 
|---|
 | 124 |         COUT(3) << "SoundManager: fade step set to " << crossFadeStep_ << std::endl; | 
|---|
 | 125 |         return; | 
|---|
 | 126 |     } | 
|---|
 | 127 |  | 
|---|
| [5929] | 128 |     void SoundManager::setListenerPosition(const Vector3& position) | 
|---|
| [3060] | 129 |     { | 
|---|
| [5929] | 130 |         alListener3f(AL_POSITION, position.x, position.y, position.z); | 
|---|
 | 131 |         ALenum error = alGetError(); | 
|---|
 | 132 |         if (error == AL_INVALID_VALUE) | 
|---|
 | 133 |             COUT(2) << "Sound: OpenAL: Invalid listener position" << std::endl; | 
|---|
| [3060] | 134 |     } | 
|---|
 | 135 |  | 
|---|
| [5929] | 136 |     void SoundManager::setListenerOrientation(const Quaternion& orientation) | 
|---|
| [3060] | 137 |     { | 
|---|
| [5929] | 138 |         // update listener orientation | 
|---|
 | 139 |         Vector3 up = orientation.xAxis(); // just a wild guess | 
|---|
 | 140 |         Vector3 at = orientation.zAxis(); | 
|---|
| [3060] | 141 |  | 
|---|
| [5929] | 142 |         ALfloat orient[6] = { at.x, at.y, at.z, | 
|---|
 | 143 |                               up.x, up.y, up.z }; | 
|---|
| [3060] | 144 |  | 
|---|
| [5929] | 145 |         alListenerfv(AL_POSITION, orient); | 
|---|
| [3060] | 146 |         ALenum error = alGetError(); | 
|---|
| [5929] | 147 |         if (error == AL_INVALID_VALUE) | 
|---|
| [3060] | 148 |             COUT(2) << "Sound: OpenAL: Invalid listener orientation" << std::endl; | 
|---|
 | 149 |     } | 
|---|
| [5982] | 150 |  | 
|---|
| [6046] | 151 |     void SoundManager::registerAmbientSound(AmbientSound* newAmbient) | 
|---|
| [5982] | 152 |     { | 
|---|
| [6069] | 153 |         if (newAmbient != NULL) | 
|---|
| [5982] | 154 |         { | 
|---|
| [6069] | 155 |             for (AmbientList::const_iterator it = this->ambientSounds_.begin(); it != this->ambientSounds_.end(); ++it) | 
|---|
| [6046] | 156 |             { | 
|---|
| [6069] | 157 |                 if (it->first == newAmbient) | 
|---|
 | 158 |                 { | 
|---|
 | 159 |                     COUT(2) << "Sound warning: Will not play an AmbientSound twice." << std::endl; | 
|---|
 | 160 |                     return; | 
|---|
 | 161 |                 } | 
|---|
| [6046] | 162 |             } | 
|---|
| [6069] | 163 |  | 
|---|
 | 164 |             if (!this->ambientSounds_.empty())  | 
|---|
 | 165 |             { | 
|---|
 | 166 |                 this->fadeOut(ambientSounds_.front().first); | 
|---|
 | 167 |             } | 
|---|
 | 168 |             this->ambientSounds_.push_front(std::make_pair(newAmbient, false)); | 
|---|
 | 169 |             newAmbient->doPlay(); | 
|---|
 | 170 |             this->fadeIn(newAmbient); | 
|---|
| [5982] | 171 |         } | 
|---|
 | 172 |     } | 
|---|
 | 173 |  | 
|---|
| [6069] | 174 |     void SoundManager::unregisterAmbientSound(AmbientSound* oldAmbient) | 
|---|
| [5982] | 175 |     { | 
|---|
| [6069] | 176 |         if (oldAmbient == NULL || ambientSounds_.empty()) | 
|---|
| [6031] | 177 |         { | 
|---|
 | 178 |             return; | 
|---|
 | 179 |         } | 
|---|
| [6069] | 180 |         if (this->ambientSounds_.front().first == oldAmbient)  | 
|---|
| [5982] | 181 |         { | 
|---|
| [6069] | 182 |             this->fadeOut(oldAmbient); | 
|---|
| [5982] | 183 |             this->ambientSounds_.pop_front(); | 
|---|
| [6069] | 184 |             if (!this->ambientSounds_.empty()) | 
|---|
| [6031] | 185 |             { | 
|---|
| [6069] | 186 |                 if (!this->ambientSounds_.front().second) // Not paused before | 
|---|
 | 187 |                 { | 
|---|
 | 188 |                     this->ambientSounds_.front().first->doPlay(); | 
|---|
 | 189 |                 } | 
|---|
 | 190 |                 this->fadeIn(this->ambientSounds_.front().first); | 
|---|
| [6031] | 191 |             } | 
|---|
| [5982] | 192 |         } | 
|---|
 | 193 |         else | 
|---|
 | 194 |         { | 
|---|
| [6069] | 195 |             for (AmbientList::iterator it = this->ambientSounds_.begin(); it != this->ambientSounds_.end(); ++it) | 
|---|
| [5982] | 196 |             { | 
|---|
| [6069] | 197 |                 if (it->first == oldAmbient) | 
|---|
| [5982] | 198 |                 { | 
|---|
| [6069] | 199 |                     this->fadeOut(oldAmbient); | 
|---|
| [5982] | 200 |                     this->ambientSounds_.erase(it); | 
|---|
 | 201 |                     break; | 
|---|
 | 202 |                 } | 
|---|
 | 203 |             } | 
|---|
 | 204 |         } | 
|---|
 | 205 |     } | 
|---|
| [6031] | 206 |  | 
|---|
| [6069] | 207 |     void SoundManager::pauseAmbientSound(AmbientSound* ambient) | 
|---|
| [6031] | 208 |     { | 
|---|
| [6069] | 209 |         if (ambient != NULL) | 
|---|
| [6031] | 210 |         { | 
|---|
| [6069] | 211 |             for (AmbientList::iterator it = this->ambientSounds_.begin(); it != this->ambientSounds_.end(); ++it) | 
|---|
 | 212 |             { | 
|---|
 | 213 |                 if (it->first == ambient) | 
|---|
 | 214 |                 { | 
|---|
 | 215 |                     it->second = true; | 
|---|
 | 216 |                     this->fadeOut(it->first); | 
|---|
 | 217 |                     return; | 
|---|
 | 218 |                 } | 
|---|
 | 219 |             } | 
|---|
| [6031] | 220 |         } | 
|---|
 | 221 |     } | 
|---|
| [6046] | 222 |  | 
|---|
| [6069] | 223 |     void SoundManager::fadeIn(AmbientSound* sound) | 
|---|
 | 224 |     { | 
|---|
 | 225 |         // If we're already fading out --> remove that | 
|---|
 | 226 |         for (std::list<AmbientSound*>::iterator it = this->fadeOutList_.begin(); it != this->fadeOutList_.end(); it++) | 
|---|
 | 227 |         { | 
|---|
 | 228 |             if (*it == sound) | 
|---|
| [6046] | 229 |             { | 
|---|
| [6069] | 230 |                 this->fadeOutList_.erase(it); | 
|---|
 | 231 |                 break; | 
|---|
| [6046] | 232 |             } | 
|---|
| [6069] | 233 |         } | 
|---|
 | 234 |         // No duplicate entries | 
|---|
 | 235 |         if (std::find(this->fadeInList_.begin(), this->fadeInList_.end(), sound) == this->fadeInList_.end()) | 
|---|
 | 236 |             this->fadeInList_.push_back(sound); | 
|---|
 | 237 |     } | 
|---|
 | 238 |  | 
|---|
 | 239 |     void SoundManager::fadeOut(AmbientSound* sound) | 
|---|
 | 240 |     { | 
|---|
 | 241 |         // If we're already fading in --> remove that | 
|---|
 | 242 |         for (std::list<AmbientSound*>::iterator it = this->fadeInList_.begin(); it != this->fadeInList_.end(); it++) | 
|---|
 | 243 |         { | 
|---|
 | 244 |             if (*it == sound) | 
|---|
| [6046] | 245 |             { | 
|---|
| [6069] | 246 |                 this->fadeInList_.erase(it); | 
|---|
 | 247 |                 break; | 
|---|
| [6046] | 248 |             } | 
|---|
 | 249 |         } | 
|---|
| [6069] | 250 |         // No duplicate entries | 
|---|
 | 251 |         if (std::find(this->fadeOutList_.begin(), this->fadeOutList_.end(), sound) == this->fadeOutList_.end()) | 
|---|
 | 252 |             this->fadeOutList_.push_back(sound); | 
|---|
| [6046] | 253 |     } | 
|---|
 | 254 |  | 
|---|
| [6069] | 255 |     void SoundManager::processCrossFading(float dt) | 
|---|
| [6046] | 256 |     { | 
|---|
| [6088] | 257 |          | 
|---|
 | 258 |         // Hacky solution to the fade delay while loading a level. | 
|---|
 | 259 |         if(dt > 0.2) | 
|---|
 | 260 |         { | 
|---|
 | 261 |             return; | 
|---|
 | 262 |         } | 
|---|
 | 263 |          | 
|---|
| [6069] | 264 |         // FADE IN | 
|---|
 | 265 |         for (std::list<AmbientSound*>::iterator it= this->fadeInList_.begin(); it != this->fadeInList_.end(); it) | 
|---|
| [6046] | 266 |         { | 
|---|
| [6069] | 267 |             if ((*it)->getVolume() + this->crossFadeStep_*dt > 1.0f) | 
|---|
| [6046] | 268 |             { | 
|---|
| [6069] | 269 |                 (*it)->setVolume(1.0f); | 
|---|
 | 270 |                 this->fadeInList_.erase(it++); | 
|---|
| [6046] | 271 |             } | 
|---|
| [6069] | 272 |             else | 
|---|
| [6046] | 273 |             { | 
|---|
| [6069] | 274 |                 (*it)->setVolume((*it)->getVolume() + this->crossFadeStep_*dt); | 
|---|
 | 275 |                 ++it; | 
|---|
 | 276 |             } | 
|---|
 | 277 |         } | 
|---|
 | 278 |  | 
|---|
 | 279 |         // FADE OUT | 
|---|
 | 280 |         for (std::list<AmbientSound*>::iterator it = this->fadeOutList_.begin(); it != this->fadeOutList_.end(); it) | 
|---|
 | 281 |         { | 
|---|
 | 282 |             if ((*it)->getVolume() - this->crossFadeStep_*dt < 0.0f) | 
|---|
 | 283 |             { | 
|---|
 | 284 |                 (*it)->setVolume(0.0f); | 
|---|
 | 285 |  | 
|---|
 | 286 |                 // If sound is in the ambient list --> pause | 
|---|
 | 287 |                 for (AmbientList::const_iterator it2 = this->ambientSounds_.begin(); it2 != this->ambientSounds_.end(); ++it2) | 
|---|
| [6046] | 288 |                 { | 
|---|
| [6069] | 289 |                     if (it2->first == *it) | 
|---|
| [6046] | 290 |                     { | 
|---|
| [6069] | 291 |                         (*it)->doPause(); | 
|---|
| [6046] | 292 |                         break; | 
|---|
 | 293 |                     } | 
|---|
 | 294 |                 } | 
|---|
| [6069] | 295 |                 // If not pause (by loop above for instance) --> stop | 
|---|
 | 296 |                 if (!(*it)->isPaused()) | 
|---|
 | 297 |                     (*it)->doStop(); | 
|---|
 | 298 |  | 
|---|
 | 299 |                 this->fadeOutList_.erase(it++); | 
|---|
| [6046] | 300 |             } | 
|---|
| [6069] | 301 |             else | 
|---|
 | 302 |             { | 
|---|
 | 303 |                 (*it)->setVolume((*it)->getVolume() - this->crossFadeStep_*dt); | 
|---|
 | 304 |                 ++it; | 
|---|
 | 305 |             } | 
|---|
| [6046] | 306 |         } | 
|---|
 | 307 |     } | 
|---|
| [3060] | 308 | } | 
|---|