Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 6244 was 6244, checked in by rgrieder, 14 years ago

Enhanced startup and shutdown sequence of the SoundManager in respect to error handling.
Also SoundManager::getALErrorString(ALEnum) returns the error as std::string (static function).

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