git-svn-id: https://scorched3d.svn.sourceforge.net/svnroot/scorched3d/trunk/scorched...
[scorched3d/parasti.git] / src / client / sound / Sound.cpp
blobc41392ef9453c532cef5dc2e3ea2523b0e16c518
1 ////////////////////////////////////////////////////////////////////////////////
2 // Scorched3D (c) 2000-2009
3 //
4 // This file is part of Scorched3D.
5 //
6 // Scorched3D is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 2 of the License, or
9 // (at your option) any later version.
11 // Scorched3D is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with Scorched3D; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 ////////////////////////////////////////////////////////////////////////////////
21 #include <console/ConsoleRuleMethodIAdapter.h>
22 #include <common/Defines.h>
23 #include <graph/OptionsDisplay.h>
24 #include <common/Logger.h>
25 #include <sound/Sound.h>
26 #include <sound/SoundBufferFactory.h>
27 #include <sound/PlayingSoundSource.h>
28 #include <graph/OptionsDisplay.h>
29 #ifdef __DARWIN__
30 #include <OpenAL/al.h>
31 #include <OpenAL/alc.h>
32 #include <OpenAL/alut.h>
33 #else
34 #include <AL/al.h>
35 #include <AL/alc.h>
36 #include <AL/alut.h>
37 #endif
38 #include <algorithm>
40 Sound *Sound::instance_ = 0;
42 Sound *Sound::instance()
44 if (!instance_)
46 instance_ = new Sound;
49 return instance_;
52 Sound::Sound() :
53 init_(false), totalTime_(0.0f),
54 GameStateI("Sound")
56 new ConsoleRuleMethodIAdapter<Sound>(
57 this, &Sound::showSoundBuffers, "SoundBuffers");
58 new ConsoleRuleMethodIAdapterEx<Sound>(
59 this, &Sound::soundPlay, "SoundPlay",
60 ConsoleUtil::formParams(
61 ConsoleRuleParam("filename", ConsoleRuleTypeString)));
64 Sound::~Sound()
66 if (init_)
69 SourceList::iterator itor;
70 for (itor = totalSources_.begin();
71 itor != totalSources_.end();
72 itor++)
74 SoundSource *source = (*itor);
75 delete source;
79 BufferMap::iterator itor;
80 for (itor = bufferMap_.begin();
81 itor != bufferMap_.end();
82 itor++)
84 SoundBuffer *buffer = (*itor).second;
85 delete buffer;
89 ALCcontext *context = alcGetCurrentContext();
90 ALCdevice *device = alcGetContextsDevice(context);
91 alcDestroyContext(context);
92 alcCloseDevice(device);
94 init_ = false;
97 void Sound::destroy()
99 delete this;
100 instance_ = 0;
103 static char *checkString(char *x)
105 return (char *)(x?x:"null");
108 bool Sound::init(int channels)
110 ALCdevice *soundDevice = alcOpenDevice(0);
111 if (!soundDevice)
113 S3D::dialogMessage("Scorched3D", "Failed to open sound device");
114 return false;
117 // Setting the frequency seems to cause screeching and
118 // loss of stereo under linux!!
119 /*int attrlist[] =
121 ALC_FREQUENCY, 11025,
122 ALC_INVALID
123 };*/
124 ALCcontext *soundContext = alcCreateContext(soundDevice, 0);
125 if (!soundContext)
127 S3D::dialogMessage("Scorched3D", "Failed to create sound context");
128 return false;
131 alcMakeContextCurrent(soundContext);
132 alDistanceModel(AL_INVERSE_DISTANCE);
134 Logger::log(S3D::formatStringBuffer("AL_VENDOR:%s",
135 checkString((char *) alGetString(AL_VENDOR))));
136 Logger::log(S3D::formatStringBuffer("AL_VERSION:%s",
137 checkString((char *) alGetString(AL_VERSION))));
138 Logger::log(S3D::formatStringBuffer("AL_RENDERER:%s",
139 checkString((char *) alGetString(AL_RENDERER))));
140 Logger::log(S3D::formatStringBuffer("AL_EXTENSIONS:%s",
141 checkString((char *) alGetString(AL_EXTENSIONS))));
142 Logger::log(S3D::formatStringBuffer("ALC_DEVICE_SPECIFIER:%s",
143 checkString((char *) alcGetString(soundDevice, ALC_DEVICE_SPECIFIER))));
145 // Create all sound channels
146 for (int i=1; i<=OptionsDisplay::instance()->getSoundChannels(); i++)
148 SoundSource *source = new SoundSource;
149 if (!source->create())
151 S3D::dialogMessage("Scorched3D",
152 S3D::formatStringBuffer("Failed to create sound channel number %i", i));
153 return false;
155 totalSources_.push_back(source);
156 availableSources_.push_back(source);
159 init_ = true;
160 return init_;
163 void Sound::soundPlay(std::vector<ConsoleRuleValue> &values)
165 ConsoleRuleValue &fileName = values[1];
167 SoundBuffer *buffer =
168 fetchOrCreateBuffer(fileName.valueString);
169 VirtualSoundSource *source =
170 new VirtualSoundSource(10000, false, true);
171 source->setRelative();
172 source->play(buffer);
175 void Sound::showSoundBuffers()
177 // Show some debug of the current playing sounds
178 int i = 1;
179 Logger::log(S3D::formatStringBuffer("%i sounds playing, %i sources free",
180 getPlayingChannels(),
181 getAvailableChannels()));
182 PlayingSourceList::iterator itor;
183 for (itor = playingSources_.begin();
184 itor != playingSources_.end();
185 itor++, i++)
187 PlayingSoundSource *source = (*itor);
188 if (source->getVirtualSource())
190 Logger::log(S3D::formatStringBuffer("%i - %u,%f - %s%s:%s",
192 source->getVirtualSource()->getPriority(),
193 source->getVirtualSource()->getDistance(),
194 (source->getStopped()?"Finished":(source->getVirtualSource()->getPlaying()?"Playing":"Stopped")),
195 (source->getVirtualSource()->getLooping()?"(Looped)":""),
196 source->getVirtualSource()->getBuffer()->getFileName()));
198 else
200 Logger::log(S3D::formatStringBuffer("%i - Pending Removal"));
205 void Sound::simulate(const unsigned state, float frameTime)
207 // Simulate all the current sources
208 // This is only applicable for streams
209 PlayingSourceList::iterator playingitor;
210 for (playingitor = playingSources_.begin();
211 playingitor != playingSources_.end();
212 playingitor++)
214 SoundSource *source = (*playingitor)->getActualSource();
215 if (source && source->getPlaying())
217 source->simulate();
221 // Check the buffers every so often
222 totalTime_ += frameTime;
223 if (totalTime_ < 0.2f) return;
224 totalTime_ = 0.0f;
226 updateSources();
229 static inline bool lt_virt(PlayingSoundSource *p2, PlayingSoundSource *p1)
231 float dist1 = 0.0f;
232 float dist2 = 0.0f;
233 unsigned int priority1 = 0;
234 unsigned int priority2 = 0;
236 VirtualSoundSource *v1 = p1->getVirtualSource();
237 VirtualSoundSource *v2 = p2->getVirtualSource();
239 if (v1 && !p1->getStopped()) priority1 = v1->getPriority();
240 if (v2 && !p2->getStopped()) priority2 = v2->getPriority();
241 if (v1) dist1 = v1->getDistance();
242 if (v2) dist2 = v2->getDistance();
244 return (priority1 < priority2 ||
245 (priority1 == priority2 && dist1 > dist2));
248 void Sound::addPlaying(VirtualSoundSource *virt)
250 // Add the new source
251 PlayingSoundSource *source = new PlayingSoundSource(virt);
252 playingSources_.push_back(source);
253 virt->setPlayingSource(source); // Need to do this before updateSources
255 updateSources();
258 void Sound::updateSources()
260 // Update all of the distances
261 Vector listenerPosition = listener_.getPosition();
262 PlayingSourceList::iterator fitor;
263 for (fitor = playingSources_.begin();
264 fitor != playingSources_.end();
265 fitor++)
267 PlayingSoundSource *source = (*fitor);
268 if (source->getVirtualSource())
270 source->getVirtualSource()->updateDistance(listenerPosition);
274 // Sort the queue by priority and distance
275 std::sort(playingSources_.begin(), playingSources_.end(), lt_virt);
277 // Start and stop the relevant sources
278 int totalSources = (int) totalSources_.size();
279 int totalPlaying = (int) playingSources_.size();
280 int count = 0;
281 PlayingSourceList::reverse_iterator ritor;
282 for (ritor = playingSources_.rbegin();
283 ritor != playingSources_.rend();
284 ritor++, count++)
286 PlayingSoundSource *source = (*ritor);
288 bool stopSource = false;
290 // Check if we have been stopped
291 if (source->getStopped())
293 stopSource = true;
295 // Check if we should be playing
296 else if (totalPlaying - count <= totalSources)
298 if (source->getActualSource())
300 if (source->getActualSource()->getPlaying())
302 // It should be playing and is playing
304 else
306 // It should be playing, but its finished playing
307 stopSource = true;
310 else if (!source->getActualSource())
312 // Its not playing and should be playing
313 DIALOG_ASSERT(!availableSources_.empty());
314 source->setActualSource(availableSources_.back());
315 availableSources_.pop_back();
316 source->getVirtualSource()->actualPlay();
319 // We should not be playing this one
320 else
322 stopSource = true;
325 // We should not be playing this sound
326 if (stopSource)
328 // We are currently playing
329 if (source->getActualSource())
331 // Stop it
332 source->getActualSource()->stop();
333 availableSources_.push_back(source->getActualSource());
334 source->setActualSource(0);
337 // If we are not looped so stop for good
338 if (source->getVirtualSource())
340 if (!source->getVirtualSource()->getLooping())
342 source->setStopped(true);
348 // Remove any finished sources
349 while (!playingSources_.empty())
351 PlayingSoundSource *source = playingSources_.back();
352 if (source->getStopped())
354 if (source->getVirtualSource())
356 source->getVirtualSource()->setPlayingSource(0);
359 DIALOG_ASSERT(!(source->getActualSource()));
360 delete source;
361 playingSources_.pop_back();
363 else break;
366 // Tidy any managed sources that have stopped playing
367 // Managed sources are virtualsources that are not kept by the user
368 // and should be deleted if they stop playing
369 bool repeat = true;
370 while (repeat)
372 repeat = false;
373 VirtualSourceList::iterator manitor;
374 for (manitor = managedSources_.begin();
375 manitor != managedSources_.end();
376 manitor++)
378 VirtualSoundSource *source = (*manitor);
379 if (!source->getPlaying())
381 managedSources_.erase(manitor);
382 delete source;
383 repeat = true;
384 break;
390 void Sound::removePlaying(VirtualSoundSource *virt)
392 if (virt->getPlayingSource())
394 virt->getPlayingSource()->setStopped(true);
395 virt->getPlayingSource()->setVirtualSource(0);
397 virt->setPlayingSource(0);
399 updateSources();
402 void Sound::addManaged(VirtualSoundSource *source)
404 managedSources_.push_back(source);
407 int Sound::getAvailableChannels()
409 return availableSources_.size();
412 int Sound::getPlayingChannels()
414 return playingSources_.size();
417 SoundListener *Sound::getDefaultListener()
419 return &listener_;
422 SoundBuffer *Sound::createBuffer(char *fileName)
424 // Return a buffer full of sound :)
425 SoundBuffer *buffer = SoundBufferFactory::createBuffer(
426 (const char *) fileName);
427 if (!buffer)
429 S3D::dialogExit("Failed to load sound",
430 S3D::formatStringBuffer("\"%s\"", fileName));
432 delete buffer;
433 return 0;
435 return buffer;
438 SoundBuffer *Sound::fetchOrCreateBuffer(const std::string &filename)
440 BufferMap::iterator itor = bufferMap_.find(filename);
441 if (itor != bufferMap_.end())
443 return (*itor).second;
446 SoundBuffer *buffer = createBuffer((char *) filename.c_str());
447 bufferMap_[filename] = buffer;
448 return buffer;