1 ////////////////////////////////////////////////////////////////////////////////
2 // Scorched3D (c) 2000-2009
4 // This file is part of Scorched3D.
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>
30 #include <OpenAL/al.h>
31 #include <OpenAL/alc.h>
32 #include <OpenAL/alut.h>
40 Sound
*Sound::instance_
= 0;
42 Sound
*Sound::instance()
46 instance_
= new Sound
;
53 init_(false), totalTime_(0.0f
),
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
)));
69 SourceList::iterator itor
;
70 for (itor
= totalSources_
.begin();
71 itor
!= totalSources_
.end();
74 SoundSource
*source
= (*itor
);
79 BufferMap::iterator itor
;
80 for (itor
= bufferMap_
.begin();
81 itor
!= bufferMap_
.end();
84 SoundBuffer
*buffer
= (*itor
).second
;
89 ALCcontext
*context
= alcGetCurrentContext();
90 ALCdevice
*device
= alcGetContextsDevice(context
);
91 alcDestroyContext(context
);
92 alcCloseDevice(device
);
103 static char *checkString(char *x
)
105 return (char *)(x
?x
:"null");
108 bool Sound::init(int channels
)
110 ALCdevice
*soundDevice
= alcOpenDevice(0);
113 S3D::dialogMessage("Scorched3D", "Failed to open sound device");
117 // Setting the frequency seems to cause screeching and
118 // loss of stereo under linux!!
121 ALC_FREQUENCY, 11025,
124 ALCcontext
*soundContext
= alcCreateContext(soundDevice
, 0);
127 S3D::dialogMessage("Scorched3D", "Failed to create sound context");
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
));
155 totalSources_
.push_back(source
);
156 availableSources_
.push_back(source
);
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
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();
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()));
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();
214 SoundSource
*source
= (*playingitor
)->getActualSource();
215 if (source
&& source
->getPlaying())
221 // Check the buffers every so often
222 totalTime_
+= frameTime
;
223 if (totalTime_
< 0.2f
) return;
229 static inline bool lt_virt(PlayingSoundSource
*p2
, PlayingSoundSource
*p1
)
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
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();
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();
281 PlayingSourceList::reverse_iterator ritor
;
282 for (ritor
= playingSources_
.rbegin();
283 ritor
!= playingSources_
.rend();
286 PlayingSoundSource
*source
= (*ritor
);
288 bool stopSource
= false;
290 // Check if we have been stopped
291 if (source
->getStopped())
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
306 // It should be playing, but its finished playing
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
325 // We should not be playing this sound
328 // We are currently playing
329 if (source
->getActualSource())
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()));
361 playingSources_
.pop_back();
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
373 VirtualSourceList::iterator manitor
;
374 for (manitor
= managedSources_
.begin();
375 manitor
!= managedSources_
.end();
378 VirtualSoundSource
*source
= (*manitor
);
379 if (!source
->getPlaying())
381 managedSources_
.erase(manitor
);
390 void Sound::removePlaying(VirtualSoundSource
*virt
)
392 if (virt
->getPlayingSource())
394 virt
->getPlayingSource()->setStopped(true);
395 virt
->getPlayingSource()->setVirtualSource(0);
397 virt
->setPlayingSource(0);
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()
422 SoundBuffer
*Sound::createBuffer(char *fileName
)
424 // Return a buffer full of sound :)
425 SoundBuffer
*buffer
= SoundBufferFactory::createBuffer(
426 (const char *) fileName
);
429 S3D::dialogExit("Failed to load sound",
430 S3D::formatStringBuffer("\"%s\"", fileName
));
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
;