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
RevLine 
[3060]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
[6117]24 *       Kevin Young
[6244]25 *       Reto Grieder
[3060]26 *   Co-authors:
27 *      ...
28 *
29 */
30
[3196]31#include "SoundManager.h"
32
[3060]33#include <AL/alut.h>
[6117]34#include <utility>
[3060]35
[5929]36#include "util/Exception.h"
[3196]37#include "util/Math.h"
[5929]38#include "util/ScopeGuard.h"
[6117]39#include "util/Clock.h"
[6203]40#include "core/ConfigValueIncludes.h"
[5929]41#include "core/GameMode.h"
42#include "core/ScopedSingletonManager.h"
[6203]43#include "core/Resource.h"
44#include "SoundBuffer.h"
[6117]45#include "BaseSound.h"
46#include "AmbientSound.h"
[6184]47#include "WorldSound.h"
[3060]48
49namespace orxonox
50{
[5929]51    ManageScopedSingleton(SoundManager, ScopeID::Graphics, true);
[3060]52
53    SoundManager::SoundManager()
[6254]54        : effectsPoolSize_(0)
[3060]55    {
[6117]56        RegisterRootObject(SoundManager);
57
58        if (!alutInitWithoutContext(NULL, NULL))
[6244]59            ThrowException(InitialisationFailed, "Sound Error: ALUT initialisation failed: " << alutGetErrorString(alutGetError()));
[5929]60        Loki::ScopeGuard alutExitGuard = Loki::MakeGuard(&alutExit);
61
[6332]62/*
[6244]63        // Get list of available sound devices and display them
[6332]64        const char* devices = alcGetString(NULL, ALC_DEVICE_SPECIFIER);
[6298]65        char* device = new char[strlen(devices)+1];
66        strcpy(device, devices);
[6244]67        std::string renderDevice;
[6332]68        SetConfigValue(renderDevice, std::string(device)).description("Sound device used for rendering");
[6244]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;
[6332]82        this->device_ = alcOpenDevice(renderDevice.c_str());
83*/
[6298]84        this->device_ = alcOpenDevice(NULL);
[5929]85        if (this->device_ == NULL)
[3060]86        {
[6244]87            COUT(1) << "Sound: Could not open sound device. Have you installed OpenAL?" << std::endl;
[5929]88#ifdef ORXONOX_PLATFORM_WINDOWS
[6244]89            COUT(1) << "Sound: Just getting the DLL with the dependencies is not enough for Windows (esp. Windows 7)!" << std::endl;
[5929]90#endif
[6322]91            ThrowException(InitialisationFailed, "Sound Error: Could not open sound device.");
[3060]92        }
[5929]93        Loki::ScopeGuard closeDeviceGuard = Loki::MakeGuard(&alcCloseDevice, this->device_);
94
[6244]95        // Create sound context and make it the currently used one
[5929]96        this->context_ = alcCreateContext(this->device_, NULL);
97        if (this->context_ == NULL)
[6244]98            ThrowException(InitialisationFailed, "Sound Error: Could not create ALC context");
[5929]99        Loki::ScopeGuard desroyContextGuard = Loki::MakeGuard(&alcDestroyContext, this->context_);
[6244]100        if (!alcMakeContextCurrent(this->context_))
101            ThrowException(InitialisationFailed, "Sound Error: Could not use ALC context");
[5929]102
[6244]103        GameMode::setPlaysSound(true);
[6322]104        Loki::ScopeGuard resetPlaysSoundGuard = Loki::MakeGuard(&GameMode::setPlaysSound, false);
[5929]105
[6244]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;
[3060]113        else
[6244]114            COUT(2) << "Sound Warning: MIME Type retrieval failed: " << alutGetErrorString(alutGetError()) << std::endl;
[6184]115       
[6186]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;
[6117]123
124        this->setConfigValues();
[6244]125
[6322]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
[6244]149        COUT(4) << "Sound: Initialisation complete" << std::endl;
[3060]150    }
151
152    SoundManager::~SoundManager()
153    {
[5929]154        GameMode::setPlaysSound(false);
[6244]155
156        // Relieve context to destroy it
157        if (!alcMakeContextCurrent(NULL))
158            COUT(1) << "Sound Error: Could not unset ALC context" << std::endl;
[3060]159        alcDestroyContext(this->context_);
[6244]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
[3280]171        alcCloseDevice(this->device_);
[6244]172#endif
173        if (!alutExit())
174            COUT(1) << "Sound Error: Closing ALUT failed: " << alutGetErrorString(alutGetError()) << std::endl;
[3060]175    }
176
[6183]177    void SoundManager::preUpdate(const Clock& time)
[6117]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);
[6322]187
[6186]188        SetConfigValue(soundVolume_, 1.0f)
[6184]189            .description("Defines the overall volume.")
[6186]190            .callback(this, &SoundManager::checkSoundVolumeValidity);
[6322]191
[6184]192        SetConfigValue(ambientVolume_, 1.0f)
193            .description("Defines the ambient volume.")
194            .callback(this, &SoundManager::checkAmbientVolumeValidity);
[6322]195
[6184]196        SetConfigValue(effectsVolume_, 1.0f)
197            .description("Defines the effects volume.")
198            .callback(this, &SoundManager::checkEffectsVolumeValidity);
[6322]199
200        SetConfigValue(maxSources_, 1024)
201            .description("Maximum number of sources to be made available");
[6117]202    }
203
[6244]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
[6117]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    }
[6184]228   
[6186]229    bool SoundManager::checkVolumeValidity(SoundType::Value type)
[6184]230    {
[6186]231        bool valid = true;
232       
233        if(this->getVolumeInternal(type) < 0.0 || this->getVolumeInternal(type) > 1.0)
[6184]234        {
235            COUT(2) << "Sound warning: Sound volume out of range, ignoring change." << std::endl;
[6186]236            valid = false;
[6184]237        }
238       
[6186]239        this->updateVolume(type);
[6241]240        COUT(4) << "SoundManager: volume set to " << this->getVolumeInternal(type) << std::endl;
[6186]241        return valid;
[6184]242    }
243   
[6186]244    void SoundManager::checkSoundVolumeValidity()
245    {
[6197]246        if(!checkVolumeValidity(SoundType::none))
[6186]247        {
248            ResetConfigValue(soundVolume_);
249        }
250    }
251   
[6184]252    void SoundManager::checkAmbientVolumeValidity()
253    {
[6197]254        if(!checkVolumeValidity(SoundType::ambient))
[6184]255        {
256            ResetConfigValue(ambientVolume_);
257        }
258    }
259   
260    void SoundManager::checkEffectsVolumeValidity()
261    {
[6197]262        if(!checkVolumeValidity(SoundType::effects))
[6184]263        {
264            ResetConfigValue(effectsVolume_);
265        }
266    }
[6117]267
[5929]268    void SoundManager::setListenerPosition(const Vector3& position)
[3060]269    {
[5929]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;
[3060]274    }
275
[5929]276    void SoundManager::setListenerOrientation(const Quaternion& orientation)
[3060]277    {
[5929]278        // update listener orientation
[6269]279        const Vector3& direction = -orientation.zAxis();
280        const Vector3& up = orientation.yAxis();
[3060]281
[6269]282        ALfloat orient[6] = { direction.x, direction.y, direction.z, up.x, up.y, up.z };
[3060]283
[6269]284        alListenerfv(AL_ORIENTATION, orient);
[3060]285        ALenum error = alGetError();
[5929]286        if (error == AL_INVALID_VALUE)
[3060]287            COUT(2) << "Sound: OpenAL: Invalid listener orientation" << std::endl;
288    }
[6117]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;
[6349]317
[6117]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    }
[6184]360   
[6186]361   
362    void SoundManager::setVolume(float vol, SoundType::Value type)
[6184]363    {
[6186]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)
[6184]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       
[6186]380        return vol;
[6184]381    }
382   
[6186]383    void SoundManager::updateVolume(SoundType::Value type)
[6184]384    {
[6186]385        switch(type)
[6184]386        {
[6186]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;
[6184]407        }
408    }
409   
[6186]410    void SoundManager::setVolumeInternal(float vol, SoundType::Value type)
[6184]411    {
[6186]412        switch(type)
[6184]413        {
[6186]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;
[6184]425        }
426    }
427   
[6186]428    float SoundManager::getVolumeInternal(SoundType::Value type)
[6184]429    {
[6186]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        }
[6184]442    }
443   
[6186]444    float SoundManager::getVolume(SoundType::Value type) 
[6184]445    {
[6186]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);
[6184]453    }
454   
[6186]455    void SoundManager::toggleMute(SoundType::Value type)
[6184]456    {
[6186]457        bool mute = !this->mute_[type];
458        this->mute_[type] = mute;
459       
460        this->updateVolume(type);
[6184]461    }
[6186]462   
463    bool SoundManager::getMute(SoundType::Value type)
464    {
465        return this->mute_[type];
466    }
467   
[6117]468
[6349]469    void SoundManager::fadeIn(const SmartPtr<AmbientSound>& sound)
[6117]470    {
471        // If we're already fading out --> remove that
[6349]472        for (std::list<SmartPtr<AmbientSound> >::iterator it = this->fadeOutList_.begin(); it != this->fadeOutList_.end(); it++)
[6117]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
[6349]485    void SoundManager::fadeOut(const SmartPtr<AmbientSound>& sound)
[6117]486    {
487        // If we're already fading in --> remove that
[6349]488        for (std::list<SmartPtr<AmbientSound> >::iterator it = this->fadeInList_.begin(); it != this->fadeInList_.end(); it++)
[6117]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
[6349]511        for (std::list<SmartPtr<AmbientSound> >::iterator it= this->fadeInList_.begin(); it != this->fadeInList_.end(); )
[6117]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
[6349]526        for (std::list<SmartPtr<AmbientSound> >::iterator it = this->fadeOutList_.begin(); it != this->fadeOutList_.end(); )
[6117]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    }
[6203]554
[6270]555    shared_ptr<SoundBuffer> SoundManager::getSoundBuffer(const std::string& filename)
[6203]556    {
[6254]557        shared_ptr<SoundBuffer> buffer;
558        // Check active or pooled buffers
[6270]559        SoundBufferMap::const_iterator it = this->soundBuffers_.find(filename);
[6203]560        if (it != this->soundBuffers_.end())
[6254]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        }
[6203]572        else
573        {
[6232]574            try
575            {
[6332]576                buffer.reset(new SoundBuffer(filename, this->effectsPool_.end()));
[6232]577            }
578            catch (...)
579            {
580                COUT(1) << Exception::handleMessage() << std::endl;
[6254]581                return buffer;
[6232]582            }
[6270]583            this->soundBuffers_[filename] = buffer;
[6203]584        }
[6254]585        return buffer;
[6203]586    }
587
[6254]588    void SoundManager::releaseSoundBuffer(const shared_ptr<SoundBuffer>& buffer, bool bPoolBuffer)
[6203]589    {
[6254]590        // Check if others are still using the buffer
591        if (buffer.use_count() != 2)
592            return;
[6270]593        SoundBufferMap::iterator it = this->soundBuffers_.find(buffer->getFilename());
[6232]594        if (it != this->soundBuffers_.end())
[6254]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
[6270]606                    SoundBufferMap::iterator itDel = this->soundBuffers_.find(bufferDel->getFilename());
[6254]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        }
[6203]618    }
[6322]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    }
[3060]645}
Note: See TracBrowser for help on using the repository browser.