Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/presentation2/src/orxonox/sound/SoundManager.cc @ 6254

Last change on this file since 6254 was 6254, checked in by rgrieder, 15 years ago

Added sound effects pooling. This should avoid long respawns (due to sound loading) if there are no more enemies.

  • Property svn:eol-style set to native
File size: 20.6 KB
Line 
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
24 *       Kevin Young
25 *       Reto Grieder
26 *   Co-authors:
27 *      ...
28 *
29 */
30
31#include "SoundManager.h"
32
33#include <AL/alut.h>
34#include <utility>
35
36#include "util/Exception.h"
37#include "util/Math.h"
38#include "util/ScopeGuard.h"
39#include "util/Clock.h"
40#include "core/ConfigValueIncludes.h"
41#include "core/GameMode.h"
42#include "core/ScopedSingletonManager.h"
43#include "core/Resource.h"
44#include "SoundBuffer.h"
45#include "BaseSound.h"
46#include "AmbientSound.h"
47#include "WorldSound.h"
48
49namespace orxonox
50{
51    ManageScopedSingleton(SoundManager, ScopeID::Graphics, true);
52
53    SoundManager::SoundManager()
54        : effectsPoolSize_(0)
55    {
56        RegisterRootObject(SoundManager);
57
58        if (!alutInitWithoutContext(NULL, NULL))
59            ThrowException(InitialisationFailed, "Sound Error: ALUT initialisation failed: " << alutGetErrorString(alutGetError()));
60        Loki::ScopeGuard alutExitGuard = Loki::MakeGuard(&alutExit);
61
62        // Get list of available sound devices and display them
63        const char* devices = alcGetString(NULL, ALC_DEVICE_SPECIFIER);
64        std::string renderDevice;
65        SetConfigValue(renderDevice, devices).description("Sound device used for rendering");
66        COUT(4) << "Sound: Available devices: ";
67        while (true)
68        {
69            this->deviceNames_.push_back(devices);
70            COUT(4) << "\"" << devices << "\", ";
71            devices += strlen(devices) + 1;
72            if (*devices == '\0')
73                break;
74        }
75        COUT(4) << std::endl;
76
77        // Open the selected device
78        COUT(3) << "Sound: Opening device \"" << renderDevice << "\"" << std::endl;
79        this->device_ = alcOpenDevice(renderDevice.c_str());
80        if (this->device_ == NULL)
81        {
82            COUT(1) << "Sound: Could not open sound device. Have you installed OpenAL?" << std::endl;
83#ifdef ORXONOX_PLATFORM_WINDOWS
84            COUT(1) << "Sound: Just getting the DLL with the dependencies is not enough for Windows (esp. Windows 7)!" << std::endl;
85#endif
86            ThrowException(InitialisationFailed, "Sound: OpenAL error: Could not open sound device.");
87        }
88        Loki::ScopeGuard closeDeviceGuard = Loki::MakeGuard(&alcCloseDevice, this->device_);
89
90        // Create sound context and make it the currently used one
91        this->context_ = alcCreateContext(this->device_, NULL);
92        if (this->context_ == NULL)
93            ThrowException(InitialisationFailed, "Sound Error: Could not create ALC context");
94        Loki::ScopeGuard desroyContextGuard = Loki::MakeGuard(&alcDestroyContext, this->context_);
95        if (!alcMakeContextCurrent(this->context_))
96            ThrowException(InitialisationFailed, "Sound Error: Could not use ALC context");
97
98        GameMode::setPlaysSound(true);
99
100        // Get some information about the sound
101        if (const char* version = alGetString(AL_VERSION))
102            COUT(4) << "Sound: --- OpenAL Version: " << version << std::endl;
103        if (const char* vendor = alGetString(AL_VENDOR))
104            COUT(4) << "Sound: --- OpenAL Vendor : " << vendor << std::endl;
105        if (const char* types = alutGetMIMETypes(ALUT_LOADER_BUFFER))
106            COUT(4) << "Sound: --- Supported MIME Types: " << types << std::endl;
107        else
108            COUT(2) << "Sound Warning: MIME Type retrieval failed: " << alutGetErrorString(alutGetError()) << std::endl;
109
110        // Disarm guards
111        alutExitGuard.Dismiss();
112        closeDeviceGuard.Dismiss();
113        desroyContextGuard.Dismiss();
114       
115        this->setVolumeInternal(1.0, SoundType::none);
116        this->setVolumeInternal(1.0, SoundType::ambient);
117        this->setVolumeInternal(1.0, SoundType::effects);
118       
119        this->mute_[SoundType::none] = false;
120        this->mute_[SoundType::ambient] = false;
121        this->mute_[SoundType::effects] = false;
122
123        this->setConfigValues();
124
125        COUT(4) << "Sound: Initialisation complete" << std::endl;
126    }
127
128    SoundManager::~SoundManager()
129    {
130        GameMode::setPlaysSound(false);
131
132        // Relieve context to destroy it
133        if (!alcMakeContextCurrent(NULL))
134            COUT(1) << "Sound Error: Could not unset ALC context" << std::endl;
135        alcDestroyContext(this->context_);
136        if (ALCenum error = alcGetError(this->device_))
137        {
138            if (error == AL_INVALID_OPERATION)
139                COUT(1) << "Sound Error: Could not destroy ALC context because it is the current one" << std::endl;
140            else
141                COUT(1) << "Sound Error: Could not destroy ALC context because it is invalid" << std::endl;
142        }
143#ifdef AL_VERSION_1_1
144        if (!alcCloseDevice(this->device_))
145            COUT(1) << "Sound Error: Could not destroy ALC device. This might be because there are still buffers in use!" << std::endl;
146#else
147        alcCloseDevice(this->device_);
148#endif
149        if (!alutExit())
150            COUT(1) << "Sound Error: Closing ALUT failed: " << alutGetErrorString(alutGetError()) << std::endl;
151    }
152
153    void SoundManager::preUpdate(const Clock& time)
154    {
155        this->processCrossFading(time.getDeltaTime());
156    }
157
158    void SoundManager::setConfigValues()
159    {
160        SetConfigValue(crossFadeStep_, 0.2f)
161            .description("Determines how fast sounds should fade, per second.")
162            .callback(this, &SoundManager::checkFadeStepValidity);
163           
164        SetConfigValue(soundVolume_, 1.0f)
165            .description("Defines the overall volume.")
166            .callback(this, &SoundManager::checkSoundVolumeValidity);
167           
168        SetConfigValue(ambientVolume_, 1.0f)
169            .description("Defines the ambient volume.")
170            .callback(this, &SoundManager::checkAmbientVolumeValidity);
171           
172        SetConfigValue(effectsVolume_, 1.0f)
173            .description("Defines the effects volume.")
174            .callback(this, &SoundManager::checkEffectsVolumeValidity);
175    }
176
177    std::string SoundManager::getALErrorString(ALenum code)
178    {
179        switch (code)
180        {
181        case AL_NO_ERROR:          return "No error";
182        case AL_INVALID_NAME:      return "Invalid AL parameter name";
183        case AL_INVALID_ENUM:      return "Invalid AL enum";
184        case AL_INVALID_VALUE:     return "Invalid AL value";
185        case AL_INVALID_OPERATION: return "Invalid AL operation";
186        case AL_OUT_OF_MEMORY:     return "AL reports out of memory";
187        default:                   return "Unknown AL error";
188        }
189    }
190
191    void SoundManager::checkFadeStepValidity()
192    {
193        if (crossFadeStep_ <= 0.0 || crossFadeStep_ >= 1.0 )
194        {
195            COUT(2) << "Sound warning: Sound step out of range, ignoring change." << std::endl;
196            ResetConfigValue(crossFadeStep_);
197        }
198        COUT(3) << "SoundManager: fade step set to " << crossFadeStep_ << std::endl;
199        return;
200    }
201   
202    bool SoundManager::checkVolumeValidity(SoundType::Value type)
203    {
204        bool valid = true;
205       
206        if(this->getVolumeInternal(type) < 0.0 || this->getVolumeInternal(type) > 1.0)
207        {
208            COUT(2) << "Sound warning: Sound volume out of range, ignoring change." << std::endl;
209            valid = false;
210        }
211       
212        this->updateVolume(type);
213        COUT(4) << "SoundManager: volume set to " << this->getVolumeInternal(type) << std::endl;
214        return valid;
215    }
216   
217    void SoundManager::checkSoundVolumeValidity()
218    {
219        if(!checkVolumeValidity(SoundType::none))
220        {
221            ResetConfigValue(soundVolume_);
222        }
223    }
224   
225    void SoundManager::checkAmbientVolumeValidity()
226    {
227        if(!checkVolumeValidity(SoundType::ambient))
228        {
229            ResetConfigValue(ambientVolume_);
230        }
231    }
232   
233    void SoundManager::checkEffectsVolumeValidity()
234    {
235        if(!checkVolumeValidity(SoundType::effects))
236        {
237            ResetConfigValue(effectsVolume_);
238        }
239    }
240
241    void SoundManager::setListenerPosition(const Vector3& position)
242    {
243        alListener3f(AL_POSITION, position.x, position.y, position.z);
244        ALenum error = alGetError();
245        if (error == AL_INVALID_VALUE)
246            COUT(2) << "Sound: OpenAL: Invalid listener position" << std::endl;
247    }
248
249    void SoundManager::setListenerOrientation(const Quaternion& orientation)
250    {
251        // update listener orientation
252        Vector3 up = orientation.xAxis(); // just a wild guess
253        Vector3 at = orientation.zAxis();
254
255        ALfloat orient[6] = { at.x, at.y, at.z,
256                              up.x, up.y, up.z };
257
258        alListenerfv(AL_POSITION, orient);
259        ALenum error = alGetError();
260        if (error == AL_INVALID_VALUE)
261            COUT(2) << "Sound: OpenAL: Invalid listener orientation" << std::endl;
262    }
263
264    void SoundManager::registerAmbientSound(AmbientSound* newAmbient)
265    {
266        if (newAmbient != NULL)
267        {
268            for (AmbientList::const_iterator it = this->ambientSounds_.begin(); it != this->ambientSounds_.end(); ++it)
269            {
270                if (it->first == newAmbient)
271                {
272                    COUT(2) << "Sound warning: Will not play an AmbientSound twice." << std::endl;
273                    return;
274                }
275            }
276
277            if (!this->ambientSounds_.empty()) 
278            {
279                this->fadeOut(ambientSounds_.front().first);
280            }
281            this->ambientSounds_.push_front(std::make_pair(newAmbient, false));
282            newAmbient->doPlay();
283            this->fadeIn(newAmbient);
284        }
285    }
286
287    void SoundManager::unregisterAmbientSound(AmbientSound* oldAmbient)
288    {
289        if (oldAmbient == NULL || ambientSounds_.empty())
290        {
291            return;
292        }
293        if (this->ambientSounds_.front().first == oldAmbient) 
294        {
295            this->fadeOut(oldAmbient);
296            this->ambientSounds_.pop_front();
297            if (!this->ambientSounds_.empty())
298            {
299                if (!this->ambientSounds_.front().second) // Not paused before
300                {
301                    this->ambientSounds_.front().first->doPlay();
302                }
303                this->fadeIn(this->ambientSounds_.front().first);
304            }
305        }
306        else
307        {
308            for (AmbientList::iterator it = this->ambientSounds_.begin(); it != this->ambientSounds_.end(); ++it)
309            {
310                if (it->first == oldAmbient)
311                {
312                    this->fadeOut(oldAmbient);
313                    this->ambientSounds_.erase(it);
314                    break;
315                }
316            }
317        }
318    }
319
320    void SoundManager::pauseAmbientSound(AmbientSound* ambient)
321    {
322        if (ambient != NULL)
323        {
324            for (AmbientList::iterator it = this->ambientSounds_.begin(); it != this->ambientSounds_.end(); ++it)
325            {
326                if (it->first == ambient)
327                {
328                    it->second = true;
329                    this->fadeOut(it->first);
330                    return;
331                }
332            }
333        }
334    }
335   
336   
337    void SoundManager::setVolume(float vol, SoundType::Value type)
338    {
339        vol = this->checkVolumeRange(vol);
340       
341        this->setVolumeInternal(vol, type);
342       
343        this->updateVolume(type);
344    }
345   
346    float SoundManager::checkVolumeRange(float vol)
347    {
348        if(vol < 0.0 || vol > 1.0)
349        {
350            COUT(2) << "Sound warning: volume out of range, cropping value." << std::endl;
351            vol = vol > 1 ? 1 : vol;
352            vol = vol < 0 ? 0 : vol;
353        }
354       
355        return vol;
356    }
357   
358    void SoundManager::updateVolume(SoundType::Value type)
359    {
360        switch(type)
361        {
362            case SoundType::none:
363                for (ObjectList<BaseSound>::iterator it = ObjectList<BaseSound>::begin(); it != ObjectList<BaseSound>::end(); ++it)
364                {
365                    (*it)->updateVolume();
366                }
367                break;
368            case SoundType::ambient:
369                for (ObjectList<AmbientSound>::iterator it = ObjectList<AmbientSound>::begin(); it != ObjectList<AmbientSound>::end(); ++it)
370                {
371                    (*it)->updateVolume();
372                }
373                break;
374            case SoundType::effects:
375                for (ObjectList<WorldSound>::iterator it = ObjectList<WorldSound>::begin(); it != ObjectList<WorldSound>::end(); ++it)
376                {
377                    (*it)->updateVolume();
378                }
379                break;
380            default:
381                COUT(2) << "Invalid SoundType in SoundManager::updateVolume() - Not updating!" << std::endl;
382        }
383    }
384   
385    void SoundManager::setVolumeInternal(float vol, SoundType::Value type)
386    {
387        switch(type)
388        {
389            case SoundType::none:
390                this->soundVolume_ = vol;
391                break;
392            case SoundType::ambient:
393                this->ambientVolume_ = vol;
394                break;
395            case SoundType::effects:
396                this->effectsVolume_ = vol;
397                break;
398            default:
399                COUT(2) << "Invalid SoundType in SoundManager::setVolumeInternal() - Not setting any volume!" << std::endl;
400        }
401    }
402   
403    float SoundManager::getVolumeInternal(SoundType::Value type)
404    {
405        switch(type)
406        {
407            case SoundType::none:
408                return this->soundVolume_;
409            case SoundType::ambient:
410                return this->ambientVolume_;
411            case SoundType::effects:
412                return this->effectsVolume_;
413            default:
414                COUT(2) << "Invalid SoundType in SoundManager::setVolumeInternal() - Returning 0.0!" << std::endl;
415                return 0.0;
416        }
417    }
418   
419    float SoundManager::getVolume(SoundType::Value type) 
420    {
421        if(this->mute_[SoundType::none] || this->mute_[type])
422            return 0.0;
423       
424        if(type == SoundType::none)
425            return this->getVolumeInternal(type);
426       
427        return this->getVolumeInternal(SoundType::none)*this->getVolumeInternal(type);
428    }
429   
430    void SoundManager::toggleMute(SoundType::Value type)
431    {
432        bool mute = !this->mute_[type];
433        this->mute_[type] = mute;
434       
435        this->updateVolume(type);
436    }
437   
438    bool SoundManager::getMute(SoundType::Value type)
439    {
440        return this->mute_[type];
441    }
442   
443
444    void SoundManager::fadeIn(AmbientSound* sound)
445    {
446        // If we're already fading out --> remove that
447        for (std::list<AmbientSound*>::iterator it = this->fadeOutList_.begin(); it != this->fadeOutList_.end(); it++)
448        {
449            if (*it == sound)
450            {
451                this->fadeOutList_.erase(it);
452                break;
453            }
454        }
455        // No duplicate entries
456        if (std::find(this->fadeInList_.begin(), this->fadeInList_.end(), sound) == this->fadeInList_.end())
457            this->fadeInList_.push_back(sound);
458    }
459
460    void SoundManager::fadeOut(AmbientSound* sound)
461    {
462        // If we're already fading in --> remove that
463        for (std::list<AmbientSound*>::iterator it = this->fadeInList_.begin(); it != this->fadeInList_.end(); it++)
464        {
465            if (*it == sound)
466            {
467                this->fadeInList_.erase(it);
468                break;
469            }
470        }
471        // No duplicate entries
472        if (std::find(this->fadeOutList_.begin(), this->fadeOutList_.end(), sound) == this->fadeOutList_.end())
473            this->fadeOutList_.push_back(sound);
474    }
475
476    void SoundManager::processCrossFading(float dt)
477    {
478       
479        // Hacky solution to the fade delay while loading a level.
480        if(dt > 0.2)
481        {
482            return;
483        }
484       
485        // FADE IN
486        for (std::list<AmbientSound*>::iterator it= this->fadeInList_.begin(); it != this->fadeInList_.end(); )
487        {
488            if ((*it)->getVolume() + this->crossFadeStep_*dt > 1.0f)
489            {
490                (*it)->setVolume(1.0f);
491                this->fadeInList_.erase(it++);
492            }
493            else
494            {
495                (*it)->setVolume((*it)->getVolume() + this->crossFadeStep_*dt);
496                ++it;
497            }
498        }
499
500        // FADE OUT
501        for (std::list<AmbientSound*>::iterator it = this->fadeOutList_.begin(); it != this->fadeOutList_.end(); )
502        {
503            if ((*it)->getVolume() - this->crossFadeStep_*dt < 0.0f)
504            {
505                (*it)->setVolume(0.0f);
506
507                // If sound is in the ambient list --> pause
508                for (AmbientList::const_iterator it2 = this->ambientSounds_.begin(); it2 != this->ambientSounds_.end(); ++it2)
509                {
510                    if (it2->first == *it)
511                    {
512                        (*it)->doPause();
513                        break;
514                    }
515                }
516                // If not pause (by loop above for instance) --> stop
517                if (!(*it)->isPaused())
518                    (*it)->doStop();
519
520                this->fadeOutList_.erase(it++);
521            }
522            else
523            {
524                (*it)->setVolume((*it)->getVolume() - this->crossFadeStep_*dt);
525                ++it;
526            }
527        }
528    }
529
530    shared_ptr<SoundBuffer> SoundManager::getSoundBuffer(shared_ptr<ResourceInfo> fileInfo)
531    {
532        shared_ptr<SoundBuffer> buffer;
533        // Check active or pooled buffers
534        SoundBufferMap::const_iterator it = this->soundBuffers_.find(fileInfo->group + '/' + fileInfo->filename);
535        if (it != this->soundBuffers_.end())
536        {
537            buffer = it->second;
538
539            // Remove from effects pool if not active used before
540            if (buffer->poolIterator_ != this->effectsPool_.end())
541            {
542                this->effectsPoolSize_ -= buffer->getSize();
543                this->effectsPool_.erase(buffer->poolIterator_);
544                buffer->poolIterator_ = this->effectsPool_.end();
545            }
546        }
547        else
548        {
549            try
550            {
551                buffer.reset(new SoundBuffer(fileInfo));
552                buffer->poolIterator_ = this->effectsPool_.end();
553            }
554            catch (...)
555            {
556                COUT(1) << Exception::handleMessage() << std::endl;
557                return buffer;
558            }
559            this->soundBuffers_[fileInfo->group + '/' + fileInfo->filename] = buffer;
560        }
561        return buffer;
562    }
563
564    void SoundManager::releaseSoundBuffer(const shared_ptr<SoundBuffer>& buffer, bool bPoolBuffer)
565    {
566        // Check if others are still using the buffer
567        if (buffer.use_count() != 2)
568            return;
569        SoundBufferMap::iterator it = this->soundBuffers_.find(buffer->fileInfo_->group + '/' + buffer->fileInfo_->filename);
570        if (it != this->soundBuffers_.end())
571        {
572            if (bPoolBuffer)
573            {
574                // Pool already too large?
575                while (this->effectsPoolSize_ + it->second->getSize() > this->maxEffectsPoolSize_s && !this->effectsPool_.empty())
576                {
577                    shared_ptr<SoundBuffer> bufferDel = this->effectsPool_.back();
578                    this->effectsPoolSize_ -= bufferDel->getSize();
579                    bufferDel->poolIterator_ = this->effectsPool_.end();
580                    this->effectsPool_.pop_back();
581                    // Remove from buffer map too
582                    SoundBufferMap::iterator itDel = this->soundBuffers_.find(bufferDel->fileInfo_->group + '/' + bufferDel->fileInfo_->filename);
583                    if (itDel != this->soundBuffers_.end())
584                        this->soundBuffers_.erase(itDel);
585                }
586                // Put buffer into the pool
587                this->effectsPoolSize_ += it->second->getSize();
588                this->effectsPool_.push_front(it->second);
589                it->second->poolIterator_ = this->effectsPool_.begin();
590                COUT(0) << "pool size: " << this->effectsPoolSize_ << std::endl;
591            }
592            else
593                this->soundBuffers_.erase(it);
594        }
595    }
596}
Note: See TracBrowser for help on using the repository browser.