Vegastrike 0.5.1 rc1  1.0
Original sources for Vegastrike Evolved
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
SceneManager.cpp
Go to the documentation of this file.
1 //
2 // C++ Implementation: Audio::SceneManager
3 //
4 #include "SceneManager.h"
5 #include "config.h"
6 
7 #include "Renderer.h"
8 #include "RenderableSource.h"
9 #include "RenderableListener.h"
10 #include "SourceTemplate.h"
11 #include "SimpleSource.h"
12 #include "SimpleScene.h"
13 #include "Sound.h"
14 #include "SourceListener.h"
15 
16 #include <limits>
17 #include <assert.h>
18 #include <vector>
19 #include <algorithm>
20 
21 #include "utils.h"
22 #include "vs_math.h"
23 
25 
26 namespace Audio {
27 
28  namespace __impl {
29 
31  {
32  struct SourceRef {
33  SharedPtr<Source> source;
34  SharedPtr<Scene> scene;
35 
41  mutable int needsActivation : 1;
42 
43 
44  SourceRef(const SharedPtr<Source> &src, const SharedPtr<Scene> &scn) :
45  source(src),
46  scene(scn),
48  {}
49 
50  bool operator==(const SourceRef &o) const
51  {
52  // Ignore scene...
53  return (source == o.source);
54  }
55 
56  bool operator<(const SourceRef &o) const
57  {
58  // Ignore scene...
59  return (source < o.source);
60  }
61  };
62 
63  // The many required indexes
64  typedef std::map<std::string, SharedPtr<Scene> > SceneMap;
65  typedef std::set<SourceRef> SourceRefSet;
66 
69 
70  // Being rendered, they need frequent updates
72 
73  SharedPtr<Renderer> renderer;
74  SharedPtr<Listener> rootListener;
75 
76  unsigned int maxSources;
77  float minGain;
78  double maxDistance;
79 
85 
90 
92  rootListener(new Listener()),
93 
94  maxSources(16),
95  minGain(1.0 / 16384.0),
96  maxDistance(std::numeric_limits<double>::infinity()),
97 
98  lastPositionUpdateTime(-std::numeric_limits<Timestamp>::infinity()),
99  lastAttributeUpdateTime(-std::numeric_limits<Timestamp>::infinity()),
100  lastListenerUpdateTime(-std::numeric_limits<Timestamp>::infinity()),
101  lastListenerAttUpdateTime(-std::numeric_limits<Timestamp>::infinity()),
102  lastActivationTime(-std::numeric_limits<Timestamp>::infinity()),
103 
104  positionUpdateFrequency(1.0/20.0),
105  attributeUpdateFrequency(1.0/5.0),
106  listenerUpdateFrequency(1.0/30.0),
107  activationFrequency(1.0/10.0)
108  {
109  }
110  };
111 
112  };
113 
114  using namespace __impl;
115 
117  data(new SceneManagerData)
118  {
119  }
120 
122  {
123  }
124 
125  const SharedPtr<Renderer>& SceneManager::internalRenderer() const
126  {
127  if (!data->renderer.get())
128  throw Exception("No renderer");
129  return data->renderer;
130  }
131 
132  SharedPtr<Source> SceneManager::createSource(SharedPtr<Sound> sound, bool looping)
133  throw(Exception)
134  {
135  if (!internalRenderer()->owns(sound))
136  throw Exception("Invalid sound: incompatible renderers used");
137 
138  return SharedPtr<Source>( new SimpleSource(sound, looping) );
139  }
140 
141  SharedPtr<Source> SceneManager::createSource(SharedPtr<SourceTemplate> tpl)
142  throw(Exception)
143  {
144  return createSource(tpl, tpl->getSoundName());
145  }
146 
147  SharedPtr<Source> SceneManager::createSource(SharedPtr<SourceTemplate> tpl, const std::string &name)
148  throw(Exception)
149  {
150  SharedPtr<Source> source = createSource(
151  internalRenderer()->getSound(
152  name,
153  tpl->getSoundType(),
154  tpl->isStreaming() ),
155  tpl->isLooping() );
156 
157  source->setCosAngleRange( tpl->getCosAngleRange() );
158  source->setPerFrequencyRadiusRatios( tpl->getPerFrequencyRadiusRatios() );
159  source->setReferenceFreqs( tpl->getReferenceFreqs() );
160  source->setGain( tpl->getGain() );
161  source->setAttenuated( tpl->isAttenuated() );
162  source->setRelative( tpl->isRelative() );
163 
164  return source;
165  }
166 
167  void SceneManager::destroySource(SharedPtr<Source> source)
168  throw()
169  {
170  // By simply unreferencing, it should get destroyed when all references are released.
171  // Which is good for multithreading - never destroy something that is being referenced.
172 
173  // We cannot have playing sources without references within the manager
174  // Since it's stopped, it will eventually be removed from the active list if there.
175  if (source->isPlaying())
176  source->stopPlaying();
177 
178  // Remove all references to it within every scene
179  SceneManagerData::SceneMap::iterator it;
180  for (it = data->activeScenes.begin(); it != data->activeScenes.end(); ++it)
181  it->second->remove(source);
182  for (it = data->inactiveScenes.begin(); it != data->inactiveScenes.end(); ++it)
183  it->second->remove(source);
184  }
185 
186  void SceneManager::addScene(SharedPtr<Scene> scene)
188  {
189  if ( data->activeScenes.count(scene->getName())
190  || data->inactiveScenes.count(scene->getName()) )
191  throw(DuplicateObjectException(scene->getName()));
192 
193  data->inactiveScenes[scene->getName()] = scene;
194  }
195 
196  SharedPtr<Scene> SceneManager::createScene(const std::string &name)
198  {
199  SharedPtr<Scene> scenePtr(new SimpleScene(name));
200  addScene(scenePtr);
201  return scenePtr;
202  }
203 
204  SharedPtr<Scene> SceneManager::getScene(const std::string &name) const
205  throw(NotFoundException)
206  {
207  SceneManagerData::SceneMap::const_iterator it;
208 
209  it = data->activeScenes.find(name);
210  if (it != data->activeScenes.end())
211  return it->second;
212 
213  it = data->inactiveScenes.find(name);
214  if (it != data->inactiveScenes.end())
215  return it->second;
216 
217  throw(NotFoundException(name));
218  }
219 
220  void SceneManager::destroyScene(const std::string &name)
221  throw(NotFoundException)
222  {
223  // By simply unreferencing, it should get destroyed when all references are released.
224  // Which is good for multithreading - never destroy something that is being referenced.
225  // Any active sources will get deactivated in the next update since there aren't any active scenes
226  // containing them.
227  data->activeScenes.erase(name);
228  data->inactiveScenes.erase(name);
229  }
230 
231  void SceneManager::setSceneActive(const std::string &name, bool active)
232  throw(NotFoundException)
233  {
234  // Simply move the pointer from one map to the other.
235  // The next update will take care of activating sources as necessary.
236  SharedPtr<Scene> scene = getScene(name);
237  if (active) {
238  data->inactiveScenes.erase(name);
239  data->activeScenes[name] = scene;
240  } else {
241  data->activeScenes.erase(name);
242  data->inactiveScenes[name] = scene;
243  }
244  }
245 
246  bool SceneManager::getSceneActive(const std::string &name)
247  throw(NotFoundException)
248  {
249  return data->activeScenes.count(name) > 0;
250  }
251 
252  void SceneManager::setRenderer(SharedPtr<Renderer> renderer)
253  throw(Exception)
254  {
255  if (data->renderer.get()) {
256  // Detach all active sources
257  for (SceneManagerData::SourceRefSet::const_iterator it = data->activeSources.begin(); it != data->activeSources.end(); ++it)
258  data->renderer->detach(it->source);
259 
260  // Detach the root listener
261  data->renderer->detach(data->rootListener);
262  }
263 
264  // Swap renderers
265  data->renderer.swap(renderer);
266 
267  if (data->renderer.get()) {
268  // Attach the root listener
269  data->renderer->attach(data->rootListener);
270 
271  // Attach all active sources
272  for (SceneManagerData::SourceRefSet::const_iterator it = data->activeSources.begin(); it != data->activeSources.end(); ++it)
273  data->renderer->attach(it->source);
274  }
275  }
276 
277  SharedPtr<Renderer> SceneManager::getRenderer() const
278  throw()
279  {
280  return data->renderer;
281  }
282 
283  unsigned int SceneManager::getMaxSources() const
284  throw()
285  {
286  return data->maxSources;
287  }
288 
289  void SceneManager::setMaxSources(unsigned int n)
290  throw(Exception)
291  {
292  data->maxSources = n;
293  }
294 
296  SharedPtr<SourceTemplate> tpl,
297  const std::string &sceneName,
298  LVector3 position,
299  Vector3 direction,
300  Vector3 velocity,
301  Scalar radius) throw(Exception)
302  {
303  if (tpl->isLooping())
304  throw(Exception("Cannot fire a looping source and forget!"));
305 
306  SharedPtr<Source> src = createSource(tpl);
307 
308  src->setPosition(position);
309  src->setDirection(direction);
310  src->setVelocity(velocity);
311  src->setRadius(radius);
312 
313  getScene(sceneName)->add(src);
314 
315  src->startPlaying();
316  }
317 
319  SharedPtr<SourceTemplate> tpl,
320  const std::string &soundName,
321  const std::string &sceneName,
322  LVector3 position,
323  Vector3 direction,
324  Vector3 velocity,
325  Scalar radius) throw(Exception)
326  {
327  if (tpl->isLooping())
328  throw(Exception("Cannot fire a looping source and forget!"));
329 
330  SharedPtr<Source> src = createSource(tpl, soundName);
331 
332  src->setPosition(position);
333  src->setDirection(direction);
334  src->setVelocity(velocity);
335  src->setRadius(radius);
336 
337  getScene(sceneName)->add(src);
338 
339  src->startPlaying();
340  }
341 
343  throw()
344  {
345  return data->minGain;
346  }
347 
348  void SceneManager::setMinGain(float gain)
349  throw(Exception)
350  {
351  assert(gain >= 0.f);
352  data->minGain = gain;
353  }
354 
356  throw()
357  {
358  return data->maxDistance;
359  }
360 
361  void SceneManager::setMaxDistance(double distance)
362  throw(Exception)
363  {
364  assert(distance >= 0.f);
365  data->maxDistance = distance;
366  }
367 
368  SharedPtr<SceneManager::SceneIterator> SceneManager::getSceneIterator() const
369  throw()
370  {
371  return SharedPtr<SceneIterator>(
374  data->activeScenes.begin(),
375  data->activeScenes.end() ),
377  data->inactiveScenes.begin(),
378  data->inactiveScenes.end() )
379  )
380  );
381  }
382 
383  SharedPtr<SceneManager::SceneIterator> SceneManager::getActiveSceneIterator() const
384  throw()
385  {
386  return SharedPtr<SceneIterator>(
388  data->activeScenes.begin(),
389  data->activeScenes.end() ) );
390  }
391 
393  {
395  bool needActivation = ((realTime - getActivationFrequency()) >= data->lastActivationTime);
396  bool needPosUpdates = ((realTime - getPositionUpdateFrequency()) >= data->lastPositionUpdateTime);
397  bool needAttUpdates = ((realTime - getAttributeUpdateFrequency()) >= data->lastAttributeUpdateTime);
398  bool needListenerUpdate = ((realTime - getListenerUpdateFrequency()) >= data->lastListenerUpdateTime);
399  bool needListenerAttUpdate = ((realTime - getAttributeUpdateFrequency()) >= data->lastListenerAttUpdateTime);
400 
401  // If we have an activation phase, in order for it to be effective
402  // we'll need an update phase as well.
403  needPosUpdates = needPosUpdates || needActivation;
404 
405  internalRenderer()->beginTransaction();
406 
407  if (needActivation) {
408  activationPhaseImpl();
409 
410  data->lastActivationTime = realTime;
411  }
412 
413  if (needListenerUpdate) {
414  updateListenerImpl(needListenerAttUpdate);
415 
416  data->lastListenerUpdateTime = realTime;
417  if (needListenerAttUpdate)
418  data->lastListenerAttUpdateTime = realTime;
419  }
420 
421  if (needPosUpdates || needAttUpdates) {
422  updateSourcesImpl(needAttUpdates);
423 
424  data->lastPositionUpdateTime = realTime;
425  if (needAttUpdates)
426  data->lastAttributeUpdateTime = realTime;
427  }
428 
429  internalRenderer()->commitTransaction();
430  }
431 
436 
438  {
439  }
440 
442  iter(itr),
443  scene(scn),
444  gain( estimateGain(**iter, listener) )
445  {
446  }
447 
448  bool operator<(const SourcePriorityRef &o) const
449  {
450  return gain > o.gain;
451  }
452  };
453 
455  throw(Exception)
456  {
457  // Just clear the active source set and recreate it from scratch.
458  // Use a "source ref heap" to find the most relevant sources (using the approximated
459  // intensity as priority). Since the heap will copy things all over, use cheap
460  // "SourceIterator"s as entries. These are SimpleScene-specific, so any subclass of
461  // SceneManager will probably want to override the activation phase.
462 
463  const SharedPtr<Renderer> &renderer = internalRenderer();
464 
465  LScalar maxDistanceSq = data->maxDistance * data->maxDistance;
466 
467  std::vector<SourcePriorityRef> selection;
468  bool heapified = false;
469  selection.reserve(data->maxSources+1);
470 
471  for (SceneManagerData::SceneMap::iterator it = data->activeScenes.begin();
472  it != data->activeScenes.end();
473  ++it)
474  {
475  SimpleScene *scene = dynamic_cast<SimpleScene*>(it->second.get());
476  Listener &listener = scene->getListener();
477 
478  for (SimpleScene::SourceIterator sit = scene->getActiveSources(),
479  send = scene->getActiveSourcesEnd();
480  sit != send;
481  ++sit)
482  {
483  if ( (*sit)->getSourceListener().get() ) {
484  // Must invoke the listener to get updated positions
485  (*sit)->getSourceListener()->onUpdate(**sit, RenderableSource::UPDATE_LOCATION);
486  }
487 
488  if (listener.getPosition().distanceSquared((*sit)->getPosition()) < maxDistanceSq) {
489  SourcePriorityRef ref(sit, listener, scene);
490  if (ref.gain > data->minGain) {
491  selection.push_back(ref);
492  if (selection.size() > data->maxSources) {
493  if (!heapified) {
494  make_heap(selection.begin(), selection.end());
495  heapified = true;
496  } else {
497  push_heap(selection.begin(), selection.end());
498  }
499  while (selection.size() > data->maxSources) {
500  pop_heap(selection.begin(), selection.end());
501  selection.resize( selection.size()-1 );
502  }
503  }
504  }
505  }
506  }
507  }
508 
510  for (std::vector<SourcePriorityRef>::const_iterator it = selection.begin(); it != selection.end(); ++it) {
511  newSources.insert(
513  *(it->iter),
514  dynamic_cast<SimpleScene*>(it->scene)->shared_from_this()
515  ) );
516  }
517 
518  // Detach deactivated sources
519  for (SceneManagerData::SourceRefSet::iterator sit = data->activeSources.begin(); sit != data->activeSources.end(); ++sit) {
520  if (newSources.find(*sit) == newSources.end())
521  renderer->detach(sit->source);
522  }
523 
524  // Attach newly activated sources, detach and remove finished ones
525  for (SceneManagerData::SourceRefSet::iterator nit = newSources.begin(); nit != newSources.end(); ) {
526  bool erase = false;
527  if (data->activeSources.find(*nit) == data->activeSources.end()) {
528  // Newly activated source
529  renderer->attach(nit->source);
530  nit->needsActivation = true;
531  } else {
532  // Pre-existing source - check if it's finished
533  if (!nit->source->getRenderable()->isPlaying()) {
534  // Give the renderable an opportunity to restart itself
535  // (by calling update without any update flag set)
536  nit->source->getRenderable()->update(0, nit->scene->getListener());
537 
538  if (!nit->source->getRenderable()->isPlaying()) {
539  // Finished - detach stop and remove
540  renderer->detach(nit->source);
541  nit->source->stopPlaying();
542  erase = true;
543 
544  // Check if it has a listener, notify in that case
545  SharedPtr<SourceListener> listener = nit->source->getSourceListener();
546  if (listener.get() != NULL && listener->wantPlayEvents()) {
547  listener->onEndOfStream(*nit->source);
548  }
549  }
550  }
551  }
552  if (erase)
553  newSources.erase(nit++);
554  else
555  ++nit;
556  }
557 
558  // Swap sets
559  data->activeSources.swap(newSources);
560  }
561 
562  void SceneManager::updateSourcesImpl(bool withAttributes)
563  throw(Exception)
564  {
565  // Two-pass stuff.
566 
567  // First, update attributes (mostly location)
568  RenderableSource::UpdateFlags updateFlags =
569  withAttributes ?
572  for (SceneManagerData::SourceRefSet::const_iterator it = data->activeSources.begin(); it != data->activeSources.end(); ++it) {
573  // Update the renderable (attributes)
574  it->source->updateRenderable(
575  updateFlags | (it->needsActivation ? RenderableSource::UPDATE_ALL : 0),
576  it->scene->getListener());
577 
578  // Then, start playing if not playing and should be playing
579  if (it->needsActivation) {
580  it->source->getRenderable()->startPlaying(
581  it->source->getWouldbePlayingTime() );
582  it->needsActivation = false;
583  }
584  }
585  }
586 
587  void SceneManager::updateListenerImpl(bool withAttributes)
588  throw(Exception)
589  {
590  // Update root listener
591  RenderableListener::UpdateFlags updateFlags =
592  withAttributes ?
595  if (data->rootListener.get())
596  data->rootListener->update(updateFlags);
597 
598  // And all scene listeners
599  for (SceneManagerData::SceneMap::const_iterator i = data->activeScenes.begin(); i != data->activeScenes.end(); ++i)
600  i->second->getListener().update(updateFlags);
601  }
602 
604  throw()
605  {
606  return data->positionUpdateFrequency;
607  }
608 
610  throw()
611  {
612  return data->listenerUpdateFrequency;
613  }
614 
616  throw()
617  {
618  return data->attributeUpdateFrequency;
619  }
620 
622  throw()
623  {
624  return data->activationFrequency;
625  }
626 
628  throw()
629  {
630  data->positionUpdateFrequency = interval;
631  }
632 
634  throw()
635  {
636  data->listenerUpdateFrequency = interval;
637  }
638 
640  throw()
641  {
642  data->attributeUpdateFrequency = interval;
643  }
644 
646  throw()
647  {
648  data->activationFrequency = interval;
649  }
650 
651  SharedPtr<Listener> SceneManager::getRootListener() const
652  throw()
653  {
654  return data->rootListener;
655  }
656 
657  void SceneManager::notifySourcePlaying(SharedPtr<Source> source, SharedPtr<Scene> scene, bool playing)
658  throw(Exception)
659  {
660  // If the source is within maxDistance from its scene's listener,
661  // schedule an immediate activation phase
662  double maxDistanceSq = getMaxDistance();
663  maxDistanceSq *= maxDistanceSq;
664 
665  if (scene->getListener().getPosition().distanceSquared(source->getPosition()) <= maxDistanceSq)
666  data->lastActivationTime = -std::numeric_limits<Timestamp>::infinity();
667  }
668 
669 };
670