Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/consolecommands/src/orxonox/sound/SoundManager.cc @ 6795

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

Improved SoundBuffer class design and removed its pooling property (just a boolean).

  • Property svn:eol-style set to native
File size: 22.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    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        {
317            return;
318        }
319        if (this->ambientSounds_.front().first == oldAmbient) 
320        {
321            this->fadeOut(oldAmbient);
322            this->ambientSounds_.pop_front();
323            if (!this->ambientSounds_.empty())
324            {
325                if (!this->ambientSounds_.front().second) // Not paused before
326                {
327                    this->ambientSounds_.front().first->doPlay();
328                }
329                this->fadeIn(this->ambientSounds_.front().first);
330            }
331        }
332        else
333        {
334            for (AmbientList::iterator it = this->ambientSounds_.begin(); it != this->ambientSounds_.end(); ++it)
335            {
336                if (it->first == oldAmbient)
337                {
338                    this->fadeOut(oldAmbient);
339                    this->ambientSounds_.erase(it);
340                    break;
341                }
342            }
343        }
344    }
345
346    void SoundManager::pauseAmbientSound(AmbientSound* ambient)
347    {
348        if (ambient != NULL)
349        {
350            for (AmbientList::iterator it = this->ambientSounds_.begin(); it != this->ambientSounds_.end(); ++it)
351            {
352                if (it->first == ambient)
353                {
354                    it->second = true;
355                    this->fadeOut(it->first);
356                    return;
357                }
358            }
359        }
360    }
361   
362   
363    void SoundManager::setVolume(float vol, SoundType::Value type)
364    {
365        vol = this->checkVolumeRange(vol);
366       
367        this->setVolumeInternal(vol, type);
368       
369        this->updateVolume(type);
370    }
371   
372    float SoundManager::checkVolumeRange(float vol)
373    {
374        if(vol < 0.0 || vol > 1.0)
375        {
376            COUT(2) << "Sound warning: volume out of range, cropping value." << std::endl;
377            vol = vol > 1 ? 1 : vol;
378            vol = vol < 0 ? 0 : vol;
379        }
380       
381        return vol;
382    }
383   
384    void SoundManager::updateVolume(SoundType::Value type)
385    {
386        switch(type)
387        {
388            case SoundType::none:
389                for (ObjectList<BaseSound>::iterator it = ObjectList<BaseSound>::begin(); it != ObjectList<BaseSound>::end(); ++it)
390                {
391                    (*it)->updateVolume();
392                }
393                break;
394            case SoundType::ambient:
395                for (ObjectList<AmbientSound>::iterator it = ObjectList<AmbientSound>::begin(); it != ObjectList<AmbientSound>::end(); ++it)
396                {
397                    (*it)->updateVolume();
398                }
399                break;
400            case SoundType::effects:
401                for (ObjectList<WorldSound>::iterator it = ObjectList<WorldSound>::begin(); it != ObjectList<WorldSound>::end(); ++it)
402                {
403                    (*it)->updateVolume();
404                }
405                break;
406            default:
407                COUT(2) << "Invalid SoundType in SoundManager::updateVolume() - Not updating!" << std::endl;
408        }
409    }
410   
411    void SoundManager::setVolumeInternal(float vol, SoundType::Value type)
412    {
413        switch(type)
414        {
415            case SoundType::none:
416                this->soundVolume_ = vol;
417                break;
418            case SoundType::ambient:
419                this->ambientVolume_ = vol;
420                break;
421            case SoundType::effects:
422                this->effectsVolume_ = vol;
423                break;
424            default:
425                COUT(2) << "Invalid SoundType in SoundManager::setVolumeInternal() - Not setting any volume!" << std::endl;
426        }
427    }
428   
429    float SoundManager::getVolumeInternal(SoundType::Value type)
430    {
431        switch(type)
432        {
433            case SoundType::none:
434                return this->soundVolume_;
435            case SoundType::ambient:
436                return this->ambientVolume_;
437            case SoundType::effects:
438                return this->effectsVolume_;
439            default:
440                COUT(2) << "Invalid SoundType in SoundManager::setVolumeInternal() - Returning 0.0!" << std::endl;
441                return 0.0;
442        }
443    }
444   
445    float SoundManager::getVolume(SoundType::Value type) 
446    {
447        if(this->mute_[SoundType::none] || this->mute_[type])
448            return 0.0;
449       
450        if(type == SoundType::none)
451            return this->getVolumeInternal(type);
452       
453        return this->getVolumeInternal(SoundType::none)*this->getVolumeInternal(type);
454    }
455   
456    void SoundManager::toggleMute(SoundType::Value type)
457    {
458        bool mute = !this->mute_[type];
459        this->mute_[type] = mute;
460       
461        this->updateVolume(type);
462    }
463   
464    bool SoundManager::getMute(SoundType::Value type)
465    {
466        return this->mute_[type];
467    }
468   
469
470    void SoundManager::fadeIn(AmbientSound* sound)
471    {
472        // If we're already fading out --> remove that
473        for (std::list<AmbientSound*>::iterator it = this->fadeOutList_.begin(); it != this->fadeOutList_.end(); it++)
474        {
475            if (*it == sound)
476            {
477                this->fadeOutList_.erase(it);
478                break;
479            }
480        }
481        // No duplicate entries
482        if (std::find(this->fadeInList_.begin(), this->fadeInList_.end(), sound) == this->fadeInList_.end())
483            this->fadeInList_.push_back(sound);
484    }
485
486    void SoundManager::fadeOut(AmbientSound* sound)
487    {
488        // If we're already fading in --> remove that
489        for (std::list<AmbientSound*>::iterator it = this->fadeInList_.begin(); it != this->fadeInList_.end(); it++)
490        {
491            if (*it == sound)
492            {
493                this->fadeInList_.erase(it);
494                break;
495            }
496        }
497        // No duplicate entries
498        if (std::find(this->fadeOutList_.begin(), this->fadeOutList_.end(), sound) == this->fadeOutList_.end())
499            this->fadeOutList_.push_back(sound);
500    }
501
502    void SoundManager::processCrossFading(float dt)
503    {
504       
505        // Hacky solution to the fade delay while loading a level.
506        if(dt > 0.2)
507        {
508            return;
509        }
510       
511        // FADE IN
512        for (std::list<AmbientSound*>::iterator it= this->fadeInList_.begin(); it != this->fadeInList_.end(); )
513        {
514            if ((*it)->getVolume() + this->crossFadeStep_*dt > 1.0f)
515            {
516                (*it)->setVolume(1.0f);
517                this->fadeInList_.erase(it++);
518            }
519            else
520            {
521                (*it)->setVolume((*it)->getVolume() + this->crossFadeStep_*dt);
522                ++it;
523            }
524        }
525
526        // FADE OUT
527        for (std::list<AmbientSound*>::iterator it = this->fadeOutList_.begin(); it != this->fadeOutList_.end(); )
528        {
529            if ((*it)->getVolume() - this->crossFadeStep_*dt < 0.0f)
530            {
531                (*it)->setVolume(0.0f);
532
533                // If sound is in the ambient list --> pause
534                for (AmbientList::const_iterator it2 = this->ambientSounds_.begin(); it2 != this->ambientSounds_.end(); ++it2)
535                {
536                    if (it2->first == *it)
537                    {
538                        (*it)->doPause();
539                        break;
540                    }
541                }
542                // If not pause (by loop above for instance) --> stop
543                if (!(*it)->isPaused())
544                    (*it)->doStop();
545
546                this->fadeOutList_.erase(it++);
547            }
548            else
549            {
550                (*it)->setVolume((*it)->getVolume() - this->crossFadeStep_*dt);
551                ++it;
552            }
553        }
554    }
555
556    shared_ptr<SoundBuffer> SoundManager::getSoundBuffer(const std::string& filename)
557    {
558        shared_ptr<SoundBuffer> buffer;
559        // Check active or pooled buffers
560        SoundBufferMap::const_iterator it = this->soundBuffers_.find(filename);
561        if (it != this->soundBuffers_.end())
562        {
563            buffer = it->second;
564
565            // Remove from effects pool if not active used before
566            if (buffer->poolIterator_ != this->effectsPool_.end())
567            {
568                this->effectsPoolSize_ -= buffer->getSize();
569                this->effectsPool_.erase(buffer->poolIterator_);
570                buffer->poolIterator_ = this->effectsPool_.end();
571            }
572        }
573        else
574        {
575            try
576            {
577                buffer.reset(new SoundBuffer(filename, this->effectsPool_.end()));
578            }
579            catch (...)
580            {
581                COUT(1) << Exception::handleMessage() << std::endl;
582                return buffer;
583            }
584            this->soundBuffers_[filename] = buffer;
585        }
586        return buffer;
587    }
588
589    void SoundManager::releaseSoundBuffer(const shared_ptr<SoundBuffer>& buffer, bool bPoolBuffer)
590    {
591        // Check if others are still using the buffer
592        if (buffer.use_count() != 2)
593            return;
594        SoundBufferMap::iterator it = this->soundBuffers_.find(buffer->getFilename());
595        if (it != this->soundBuffers_.end())
596        {
597            if (bPoolBuffer)
598            {
599                // Pool already too large?
600                while (this->effectsPoolSize_ + it->second->getSize() > this->maxEffectsPoolSize_s && !this->effectsPool_.empty())
601                {
602                    shared_ptr<SoundBuffer> bufferDel = this->effectsPool_.back();
603                    this->effectsPoolSize_ -= bufferDel->getSize();
604                    bufferDel->poolIterator_ = this->effectsPool_.end();
605                    this->effectsPool_.pop_back();
606                    // Remove from buffer map too
607                    SoundBufferMap::iterator itDel = this->soundBuffers_.find(bufferDel->getFilename());
608                    if (itDel != this->soundBuffers_.end())
609                        this->soundBuffers_.erase(itDel);
610                }
611                // Put buffer into the pool
612                this->effectsPoolSize_ += it->second->getSize();
613                this->effectsPool_.push_front(it->second);
614                it->second->poolIterator_ = this->effectsPool_.begin();
615            }
616            else
617                this->soundBuffers_.erase(it);
618        }
619    }
620
621    ALuint SoundManager::getSoundSource()
622    {
623        if (!this->soundSources_.empty())
624        {
625            ALuint source = this->soundSources_.back();
626            this->soundSources_.pop_back();
627            return source;
628        }
629        else
630        {
631            // Return no source ID
632            ALuint source = 123456789;
633            while (alIsSource(++source));
634            return source;
635        }
636    }
637
638    void SoundManager::releaseSoundSource(ALuint source)
639    {
640#ifndef NDEBUG
641        for (std::vector<ALuint>::const_iterator it = this->soundSources_.begin(); it != this->soundSources_.end(); ++it)
642            assert((*it) != source);
643#endif
644        this->soundSources_.push_back(source);
645    }
646}
Note: See TracBrowser for help on using the repository browser.