Sort include order.
[gemrb.git] / gemrb / plugins / OpenALAudio / AmbientMgrAL.cpp
blobc5c6c53c3d0d81f68ee70bb184b999f7f4421745
1 /* GemRB - Infinity Engine Emulator
2 * Copyright (C) 2004 The GemRB Project
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 #include "AmbientMgrAL.h"
23 #include "OpenALAudio.h"
25 #include "Ambient.h"
26 #include "Game.h"
27 #include "Interface.h"
29 #include <cassert>
30 #include <climits>
31 #include <cmath>
32 #include <cstdio>
34 #include <SDL.h>
36 // TODO: remove last dependencies on OpenAL, and then rename and move it?
38 // legal nop if already reset
39 void AmbientMgrAL::reset()
41 if (NULL != player){
42 SDL_mutexP(mutex);
44 for (std::vector<AmbientSource *>::iterator it = ambientSources.begin(); it != ambientSources.end(); ++it) {
45 delete (*it);
47 ambientSources.clear();
48 AmbientMgr::reset();
49 if (NULL != player) {
50 SDL_CondSignal(cond);
51 SDL_mutexV(mutex);
52 SDL_WaitThread(player, NULL);
53 player = NULL;
57 void AmbientMgrAL::setAmbients(const std::vector<Ambient *> &a)
59 AmbientMgr::setAmbients(a);
60 assert(NULL == player);
62 ambientSources.reserve(a.size());
63 for (std::vector<Ambient *>::const_iterator it = a.begin(); it != a.end(); ++it) {
64 ambientSources.push_back(new AmbientSource(*it));
66 core->GetAudioDrv()->UpdateVolume( GEM_SND_VOL_AMBIENTS );
68 player = SDL_CreateThread(&play, (void *) this);
71 void AmbientMgrAL::activate(const std::string &name)
73 if (NULL != player)
74 SDL_mutexP(mutex);
75 AmbientMgr::activate(name);
76 if (NULL != player) {
77 SDL_CondSignal(cond);
78 SDL_mutexV(mutex);
82 void AmbientMgrAL::activate()
84 if (NULL != player)
85 SDL_mutexP(mutex);
86 AmbientMgr::activate();
87 if (NULL != player) {
88 SDL_CondSignal(cond);
89 SDL_mutexV(mutex);
93 void AmbientMgrAL::deactivate(const std::string &name)
95 if (NULL != player)
96 SDL_mutexP(mutex);
97 AmbientMgr::deactivate(name);
98 if (NULL != player) {
99 SDL_CondSignal(cond);
100 SDL_mutexV(mutex);
104 void AmbientMgrAL::deactivate()
106 if (NULL != player)
107 SDL_mutexP(mutex);
108 AmbientMgr::deactivate();
109 hardStop();
110 if (NULL != player)
111 SDL_mutexV(mutex);
114 void AmbientMgrAL::hardStop()
116 for (std::vector<AmbientSource *>::iterator it = ambientSources.begin(); it != ambientSources.end(); ++it) {
117 (*it)->hardStop();
121 int AmbientMgrAL::play(void *am)
123 AmbientMgrAL * ambim = (AmbientMgrAL *) am;
124 SDL_mutexP(ambim->mutex);
125 while (0 != ambim->ambientSources.size()) {
126 if (NULL == core->GetGame()) { // we don't have any game, and we need one
127 break;
129 unsigned int delay = ambim->tick(SDL_GetTicks());
130 assert(delay > 0);
131 SDL_CondWaitTimeout(ambim->cond, ambim->mutex, delay);
133 SDL_mutexV(ambim->mutex);
134 return 0;
137 unsigned int AmbientMgrAL::tick(unsigned int ticks)
139 unsigned int delay = 60000; // wait one minute if all sources are off
141 if (!active)
142 return delay;
144 int xpos, ypos;
145 core->GetAudioDrv()->GetListenerPos(xpos, ypos);
146 Point listener;
147 listener.x = (short) xpos;
148 listener.y = (short) ypos;
150 ieDword timeslice = 1<<(((core->GetGame()->GameTime / 60 + 30) / 60 - 1) % 24);
152 for (std::vector<AmbientSource *>::iterator it = ambientSources.begin(); it != ambientSources.end(); ++it) {
153 unsigned int newdelay = (*it)->tick(ticks, listener, timeslice);
154 if (newdelay < delay)
155 delay = newdelay;
157 return delay;
160 AmbientMgrAL::AmbientSource::AmbientSource(const Ambient *a)
161 : stream(-1), ambient(a), lastticks(0), enqueued(0), loaded(false)
163 // TODO: wait random amount of time before beginning?
166 void AmbientMgrAL::AmbientSource::ensureLoaded()
168 // TODO: implement this after caching in ACMImp is done
169 if (loaded) return;
171 unsigned int i=ambient->sounds.size();
172 soundrefs.reserve(i);
173 while(i--) {
174 // TODO: cache this sound
175 // (and skip it if it turns out to be invalid)
176 soundrefs.push_back(ambient->sounds[i]);
179 loaded = true;
182 AmbientMgrAL::AmbientSource::~AmbientSource()
184 if (stream >= 0) {
185 core->GetAudioDrv()->ReleaseStream(stream, true);
186 stream = -1;
190 unsigned int AmbientMgrAL::AmbientSource::tick(unsigned int ticks, Point listener, ieDword timeslice)
192 /* if we are out of sounds do nothing */
193 if(!ambient->sounds.size()) {
194 return UINT_MAX;
196 if (loaded && soundrefs.empty()) return UINT_MAX;
198 if ((! (ambient->getFlags() & IE_AMBI_ENABLED)) || (! ambient->getAppearance()&timeslice)) {
199 // disabled
201 if (stream >= 0) {
202 // release the stream without immediately stopping it
203 core->GetAudioDrv()->ReleaseStream(stream, false);
205 return UINT_MAX;
208 int delay = ambient->getInterval() * 1000;
209 int left = lastticks - ticks + delay;
210 if (0 < left) // we are still waiting
211 return left;
212 if (enqueued > 0) // we have already played that much
213 enqueued += left;
214 if (enqueued < 0)
215 enqueued = 0;
217 lastticks = ticks;
218 if (0 == delay) // it's a non-stop ambient, so in any case wait only a sec
219 delay = 1000;
221 if (! (ambient->getFlags() & IE_AMBI_MAIN) && !isHeard( listener )) { // we are out of range
222 if (delay > 500) {
223 // release stream if we're inactive for a while
224 core->GetAudioDrv()->ReleaseStream(stream);
226 return delay;
229 ensureLoaded();
230 if (soundrefs.empty()) return UINT_MAX;
232 if (stream < 0) {
233 // we need to allocate a stream
234 stream = core->GetAudioDrv()->SetupNewStream(ambient->getOrigin().x, ambient->getOrigin().y, ambient->getHeight(), ambient->getGain(), (ambient->getFlags() & IE_AMBI_POINT), true);
236 if (stream == -1) {
237 // no streams available...
238 // Try again later
239 return delay;
244 /* it seems that the following (commented out) is not the purpose of the perset field, as
245 it leads to ambients playing non-stop and queues overfilled */
246 /* int leftNum = ambient -> getPerset(); */
247 int leftNum = 1;
248 int leftMS = 0;
249 if (0 == ambient->getInterval()) {
250 leftNum = 0;
251 leftMS = 1000 - enqueued; // let's have at least 1 second worth queue
255 while (0 < leftNum || 0 < leftMS) {
256 int len = enqueue();
257 if (len < 0) break;
258 --leftNum;
259 leftMS -= len;
260 enqueued += len;
263 return delay;
266 /* enqueues a random sound and returns its length */
267 int AmbientMgrAL::AmbientSource::enqueue()
269 if (soundrefs.empty()) return -1;
270 if (stream < 0) return -1;
271 int index = rand() % soundrefs.size();
272 //printf("Playing ambient %p, %s, %d/%ld on stream %d\n", (void*)this, soundrefs[index], index, soundrefs.size(), stream);
273 return core->GetAudioDrv()->QueueAmbient(stream, soundrefs[index]);
276 bool AmbientMgrAL::AmbientSource::isHeard(const Point &listener) const
278 int xdist =listener.x - ambient->getOrigin().x;
279 int ydist =listener.y - ambient->getOrigin().y;
280 int dist = (int) sqrt( (double) (xdist * xdist + ydist * ydist) );
281 return dist < ambient->getRadius();
284 void AmbientMgrAL::AmbientSource::hardStop()
286 if (stream >= 0) {
287 core->GetAudioDrv()->ReleaseStream(stream, true);
288 stream = -1;
292 void AmbientMgrAL::UpdateVolume(unsigned short volume)
294 SDL_mutexP( mutex );
295 for (std::vector<AmbientSource *>::iterator it = ambientSources.begin(); it != ambientSources.end(); ++it) {
296 (*it) -> SetVolume( volume );
298 SDL_mutexV( mutex );
301 /* sets the overall volume (in percent)
302 * the final volume is affected by the specific ambient gain
304 void AmbientMgrAL::AmbientSource::SetVolume(unsigned short volume)
306 if (stream >= 0) {
307 int v = volume;
308 v *= ambient->getGain();
309 v /= 100;
310 core->GetAudioDrv()->SetAmbientStreamVolume(stream, v);