Sound.cpp

Go to the documentation of this file.
00001 ////////////////////////////////////////////////////////////////////////////////
00002 //    Scorched3D (c) 2000-2009
00003 //
00004 //    This file is part of Scorched3D.
00005 //
00006 //    Scorched3D is free software; you can redistribute it and/or modify
00007 //    it under the terms of the GNU General Public License as published by
00008 //    the Free Software Foundation; either version 2 of the License, or
00009 //    (at your option) any later version.
00010 //
00011 //    Scorched3D is distributed in the hope that it will be useful,
00012 //    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014 //    GNU General Public License for more details.
00015 //
00016 //    You should have received a copy of the GNU General Public License
00017 //    along with Scorched3D; if not, write to the Free Software
00018 //    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019 ////////////////////////////////////////////////////////////////////////////////
00020 
00021 #include <console/ConsoleRuleMethodIAdapter.h>
00022 #include <common/Defines.h>
00023 #include <graph/OptionsDisplay.h>
00024 #include <common/Logger.h>
00025 #include <sound/Sound.h>
00026 #include <sound/SoundBufferFactory.h>
00027 #include <sound/PlayingSoundSource.h>
00028 #include <graph/OptionsDisplay.h>
00029 #ifdef __DARWIN__
00030 #include <OpenAL/al.h>
00031 #include <OpenAL/alc.h>
00032 #include <OpenAL/alut.h>
00033 #else
00034 #include <AL/al.h>
00035 #include <AL/alc.h>
00036 #include <AL/alut.h>
00037 #endif
00038 #include <algorithm>
00039 
00040 Sound *Sound::instance_ = 0;
00041 
00042 Sound *Sound::instance()
00043 {
00044         if (!instance_)
00045         {
00046                 instance_ = new Sound;
00047         }
00048 
00049         return instance_;
00050 }
00051 
00052 Sound::Sound() : 
00053         init_(false), totalTime_(0.0f),
00054         GameStateI("Sound")
00055 {
00056         new ConsoleRuleMethodIAdapter<Sound>(
00057                 this, &Sound::showSoundBuffers, "SoundBuffers");
00058         new ConsoleRuleMethodIAdapterEx<Sound>(
00059                 this, &Sound::soundPlay, "SoundPlay", 
00060                 ConsoleUtil::formParams(
00061                 ConsoleRuleParam("filename", ConsoleRuleTypeString)));
00062 }
00063 
00064 Sound::~Sound()
00065 {
00066         if (init_)
00067         {
00068                 {
00069                         SourceList::iterator itor;
00070                         for (itor = totalSources_.begin();
00071                                 itor != totalSources_.end();
00072                                 itor++)
00073                         {
00074                                 SoundSource *source = (*itor);
00075                                 delete source;
00076                         }
00077                 }
00078                 {
00079                         BufferMap::iterator itor;
00080                         for (itor = bufferMap_.begin();
00081                                 itor != bufferMap_.end();
00082                                 itor++)
00083                         {
00084                                 SoundBuffer *buffer = (*itor).second;
00085                                 delete buffer;
00086                         }
00087                 }
00088 
00089                 ALCcontext *context = alcGetCurrentContext();
00090                 ALCdevice *device = alcGetContextsDevice(context);
00091         alcDestroyContext(context);
00092         alcCloseDevice(device);
00093         }
00094         init_ = false;
00095 }
00096 
00097 void Sound::destroy()
00098 {
00099         delete this;
00100         instance_ = 0;
00101 }
00102 
00103 static char *checkString(char *x) 
00104 {
00105         return (char *)(x?x:"null");
00106 }
00107 
00108 bool Sound::init(int channels)
00109 {
00110         ALCdevice *soundDevice = alcOpenDevice(0);
00111         if (!soundDevice)
00112         {
00113                 S3D::dialogMessage("Scorched3D", "Failed to open sound device");
00114                 return false;
00115         }
00116 
00117         // Setting the frequency seems to cause screeching and
00118         // loss of stereo under linux!!
00119         /*int attrlist[] = 
00120         { 
00121                 ALC_FREQUENCY, 11025,
00122                 ALC_INVALID
00123         };*/
00124         ALCcontext *soundContext = alcCreateContext(soundDevice, 0);
00125         if (!soundContext)
00126         {
00127                 S3D::dialogMessage("Scorched3D", "Failed to create sound context");
00128                 return false;
00129         }
00130 
00131         alcMakeContextCurrent(soundContext); 
00132         alDistanceModel(AL_INVERSE_DISTANCE);
00133 
00134         Logger::log(S3D::formatStringBuffer("AL_VENDOR:%s",
00135                 checkString((char *) alGetString(AL_VENDOR))));
00136         Logger::log(S3D::formatStringBuffer("AL_VERSION:%s",
00137                 checkString((char *) alGetString(AL_VERSION))));
00138         Logger::log(S3D::formatStringBuffer("AL_RENDERER:%s",
00139                 checkString((char *) alGetString(AL_RENDERER))));
00140         Logger::log(S3D::formatStringBuffer("AL_EXTENSIONS:%s",
00141                 checkString((char *) alGetString(AL_EXTENSIONS))));
00142         Logger::log(S3D::formatStringBuffer("ALC_DEVICE_SPECIFIER:%s",
00143                 checkString((char *) alcGetString(soundDevice, ALC_DEVICE_SPECIFIER))));
00144 
00145         // Create all sound channels
00146         for (int i=1; i<=OptionsDisplay::instance()->getSoundChannels(); i++)
00147         {
00148                 SoundSource *source = new SoundSource;
00149                 if (!source->create())
00150                 {
00151                         S3D::dialogMessage("Scorched3D", 
00152                                 S3D::formatStringBuffer("Failed to create sound channel number %i", i));
00153                         return false;
00154                 }
00155                 totalSources_.push_back(source);
00156                 availableSources_.push_back(source);
00157         }
00158 
00159         init_ = true;
00160         return init_;
00161 }
00162 
00163 void Sound::soundPlay(std::vector<ConsoleRuleValue> &values)
00164 {
00165         ConsoleRuleValue &fileName = values[1];
00166 
00167         SoundBuffer *buffer = 
00168                 fetchOrCreateBuffer(fileName.valueString);
00169         VirtualSoundSource *source = 
00170                 new VirtualSoundSource(10000, false, true);
00171         source->setRelative();
00172         source->play(buffer);
00173 }
00174 
00175 void Sound::showSoundBuffers()
00176 {
00177         // Show some debug of the current playing sounds
00178         int i = 1;
00179         Logger::log(S3D::formatStringBuffer("%i sounds playing, %i sources free",
00180                 getPlayingChannels(),
00181                 getAvailableChannels()));
00182         PlayingSourceList::iterator itor;
00183         for (itor = playingSources_.begin();
00184                 itor != playingSources_.end();
00185                 itor++, i++)
00186         {
00187                 PlayingSoundSource *source = (*itor);
00188                 if (source->getVirtualSource())
00189                 {
00190                         Logger::log(S3D::formatStringBuffer("%i - %u,%f - %s%s:%s",
00191                                 i, 
00192                                 source->getVirtualSource()->getPriority(),
00193                                 source->getVirtualSource()->getDistance(),
00194                                 (source->getStopped()?"Finished":(source->getVirtualSource()->getPlaying()?"Playing":"Stopped")),
00195                                 (source->getVirtualSource()->getLooping()?"(Looped)":""),
00196                                 source->getVirtualSource()->getBuffer()->getFileName()));
00197                 }
00198                 else
00199                 {
00200                         Logger::log(S3D::formatStringBuffer("%i - Pending Removal"));
00201                 }
00202         }
00203 }
00204 
00205 void Sound::simulate(const unsigned state, float frameTime)
00206 {
00207         // Simulate all the current sources
00208         // This is only applicable for streams
00209         PlayingSourceList::iterator playingitor;
00210         for (playingitor = playingSources_.begin();
00211                 playingitor != playingSources_.end();
00212                 playingitor++)
00213         {
00214                 SoundSource *source = (*playingitor)->getActualSource();
00215                 if (source && source->getPlaying())
00216                 {
00217                         source->simulate();
00218                 }
00219         }
00220 
00221         // Check the buffers every so often
00222         totalTime_ += frameTime;
00223         if (totalTime_ < 0.2f) return;
00224         totalTime_ = 0.0f;
00225 
00226         updateSources();
00227 }
00228 
00229 static inline bool lt_virt(PlayingSoundSource *p2, PlayingSoundSource *p1)
00230 { 
00231         float dist1 = 0.0f;
00232         float dist2 = 0.0f;
00233         unsigned int priority1 = 0;
00234         unsigned int priority2 = 0;
00235 
00236     VirtualSoundSource *v1 = p1->getVirtualSource();
00237         VirtualSoundSource *v2 = p2->getVirtualSource();
00238 
00239         if (v1 && !p1->getStopped()) priority1 = v1->getPriority();
00240         if (v2 && !p2->getStopped()) priority2 = v2->getPriority();
00241         if (v1) dist1 = v1->getDistance();
00242         if (v2) dist2 = v2->getDistance();
00243 
00244         return (priority1 < priority2 ||
00245                 (priority1 == priority2 && dist1 > dist2));
00246 }
00247 
00248 void Sound::addPlaying(VirtualSoundSource *virt)
00249 {
00250         // Add the new source
00251         PlayingSoundSource *source = new PlayingSoundSource(virt);
00252         playingSources_.push_back(source);
00253         virt->setPlayingSource(source); // Need to do this before updateSources
00254 
00255         updateSources();
00256 }
00257 
00258 void Sound::updateSources()
00259 {
00260         // Update all of the distances
00261         Vector listenerPosition = listener_.getPosition();
00262         PlayingSourceList::iterator fitor;
00263         for (fitor = playingSources_.begin();
00264                 fitor != playingSources_.end();
00265                 fitor++)
00266         {
00267                 PlayingSoundSource *source = (*fitor);
00268                 if (source->getVirtualSource())
00269                 {
00270                         source->getVirtualSource()->updateDistance(listenerPosition);
00271                 }
00272         }
00273 
00274         // Sort the queue by priority and distance
00275         std::sort(playingSources_.begin(), playingSources_.end(), lt_virt); 
00276 
00277         // Start and stop the relevant sources
00278         int totalSources = (int) totalSources_.size();
00279         int totalPlaying = (int) playingSources_.size();
00280         int count = 0;
00281         PlayingSourceList::reverse_iterator ritor;
00282         for (ritor = playingSources_.rbegin();
00283                 ritor != playingSources_.rend();
00284                 ritor++, count++)
00285         {
00286                 PlayingSoundSource *source = (*ritor);
00287 
00288                 bool stopSource = false;
00289 
00290                 // Check if we have been stopped
00291                 if (source->getStopped())
00292                 {
00293                         stopSource = true;
00294                 }
00295                 // Check if we should be playing
00296                 else if (totalPlaying - count <= totalSources)
00297                 {
00298                         if (source->getActualSource())
00299                         {
00300                                 if (source->getActualSource()->getPlaying())
00301                                 {
00302                                         // It should be playing and is playing
00303                                 }
00304                                 else
00305                                 {
00306                                         // It should be playing, but its finished playing
00307                                         stopSource = true;
00308                                 }
00309                         }
00310                         else if (!source->getActualSource())
00311                         {
00312                                 // Its not playing and should be playing
00313                                 DIALOG_ASSERT(!availableSources_.empty());
00314                                 source->setActualSource(availableSources_.back());
00315                                 availableSources_.pop_back();
00316                                 source->getVirtualSource()->actualPlay();
00317                         }
00318                 }
00319                 // We should not be playing this one
00320                 else
00321                 {
00322                         stopSource = true;
00323                 }
00324 
00325                 // We should not be playing this sound
00326                 if (stopSource)
00327                 {
00328                         // We are currently playing
00329                         if (source->getActualSource())
00330                         {
00331                                 // Stop it
00332                                 source->getActualSource()->stop();
00333                                 availableSources_.push_back(source->getActualSource());
00334                                 source->setActualSource(0);
00335                         }
00336 
00337                         // If we are not looped so stop for good
00338                         if (source->getVirtualSource())
00339                         {
00340                                 if (!source->getVirtualSource()->getLooping())
00341                                 {
00342                                         source->setStopped(true);
00343                                 }
00344                         }
00345                 }
00346         }
00347 
00348         // Remove any finished sources
00349         while (!playingSources_.empty())
00350         {
00351                 PlayingSoundSource *source = playingSources_.back();
00352                 if (source->getStopped())
00353                 {
00354                         if (source->getVirtualSource())
00355                         {
00356                                 source->getVirtualSource()->setPlayingSource(0);
00357                         }
00358 
00359                         DIALOG_ASSERT(!(source->getActualSource()));
00360                         delete source;
00361                         playingSources_.pop_back();
00362                 }
00363                 else break;
00364         }
00365 
00366         // Tidy any managed sources that have stopped playing
00367         // Managed sources are virtualsources that are not kept by the user
00368         // and should be deleted if they stop playing
00369         bool repeat = true;
00370         while (repeat)
00371         {
00372                 repeat = false;
00373                 VirtualSourceList::iterator manitor;
00374                 for (manitor = managedSources_.begin();
00375                         manitor != managedSources_.end();
00376                         manitor++)
00377                 {
00378                         VirtualSoundSource *source = (*manitor);
00379                         if (!source->getPlaying())
00380                         {
00381                                 managedSources_.erase(manitor);
00382                                 delete source;
00383                                 repeat = true;
00384                                 break;
00385                         }
00386                 }
00387         }
00388 }
00389 
00390 void Sound::removePlaying(VirtualSoundSource *virt)
00391 {
00392         if (virt->getPlayingSource())
00393         {
00394                 virt->getPlayingSource()->setStopped(true);
00395                 virt->getPlayingSource()->setVirtualSource(0);
00396         }
00397         virt->setPlayingSource(0);
00398 
00399         updateSources();
00400 }
00401 
00402 void Sound::addManaged(VirtualSoundSource *source)
00403 {
00404         managedSources_.push_back(source);
00405 }
00406 
00407 int Sound::getAvailableChannels()
00408 {
00409         return availableSources_.size();
00410 }
00411 
00412 int Sound::getPlayingChannels()
00413 {
00414         return playingSources_.size();
00415 }
00416 
00417 SoundListener *Sound::getDefaultListener()
00418 {
00419         return &listener_;
00420 }
00421 
00422 SoundBuffer *Sound::createBuffer(char *fileName)
00423 {
00424         // Return a buffer full of sound :)
00425         SoundBuffer *buffer = SoundBufferFactory::createBuffer(
00426                 (const char *) fileName);
00427         if (!buffer)
00428         {
00429                 S3D::dialogExit("Failed to load sound",
00430                         S3D::formatStringBuffer("\"%s\"", fileName));
00431 
00432                 delete buffer;
00433                 return 0;
00434         }
00435         return buffer;
00436 }
00437 
00438 SoundBuffer *Sound::fetchOrCreateBuffer(const std::string &filename)
00439 {
00440         BufferMap::iterator itor = bufferMap_.find(filename);
00441         if (itor != bufferMap_.end())
00442         {
00443                 return (*itor).second;
00444         }
00445 
00446         SoundBuffer *buffer = createBuffer((char *) filename.c_str());
00447         bufferMap_[filename] = buffer;
00448         return buffer;
00449 }

Generated on Mon Feb 16 15:14:47 2009 for Scorched3D by  doxygen 1.5.3