Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/output/src/orxonox/sound/SoundManager.cc @ 8830

Last change on this file since 8830 was 8830, checked in by landauf, 13 years ago

added some output (user and internal) throughout the initialization of the game, graphics, and game states

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