Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

Overriding preDestroy() in AmbientSound to make them fade out when the level unloads.
@Kevin: this should also prevent the random bug you showed me when reloading a level with ambient sound.

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