Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

Added audio source management. This should reduce the problems when loading too many sounds.
However if there are too many players shooting at the same time, some sounds may still not play.

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