Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/sound4/src/orxonox/sound/SoundManager.cc @ 6435

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

Moved getALErrorString function from SoundManager.h to newly created SoundPrereqs.h file.
Also moved forward declarations to that file (from OrxonoxPrereqs.h).

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