Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

Added config value to disable sound entirely.
Also added InitialisationAbortedException to be used by singletons in order not to be constructed, but nicely telling the user (only for those singletons who are allowed to fail!).

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