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
OpenALRenderer.cpp
Go to the documentation of this file.
1 //
2 // C++ Implementation: Audio::Renderer
3 //
4 
5 #include "OpenALRenderer.h"
7 #include "OpenALHelpers.h"
8 #include "config.h"
9 
10 #include "al.h"
11 
12 #include "../../Sound.h"
13 #include "../../Source.h"
14 #include "../../Listener.h"
15 
16 #include "OpenALSimpleSound.h"
17 #include "OpenALStreamingSound.h"
18 
20 #include "OpenALRenderableSource.h"
22 
23 namespace Audio {
24 
25  namespace __impl {
26 
27  namespace OpenAL {
28 
29  struct RendererData
30  {
31  // The many required indexes
32  ALCdevice *alDevice;
33  ALCcontext *alContext;
34 
35  class SoundKey {
37  size_t nameLen;
38  std::string name;
39  public:
40  SoundKey() :
41  type(VSFileSystem::SoundFile),
42  nameLen(0)
43  {}
44  SoundKey(VSFileSystem::VSFileType _type, const std::string &_name) :
45  type(_type),
46  nameLen(_name.length()),
47  name(_name)
48  {}
49 
50  bool isNull() const { return nameLen == 0; }
51 
52  bool operator<(const SoundKey &other) const
53  {
54  return (type < other.type) ||
55  ((type == other.type) && (
56  (nameLen < other.nameLen) ||
57  ((nameLen == other.nameLen) && (
58  name < other.name
59  ))
60  ));
61  }
62 
63  bool operator==(const SoundKey &other) const
64  {
65  return (type == other.type) &&
66  (nameLen == other.nameLen) &&
67  (name == other.name);
68  }
69  };
70 
71  typedef std::map<SoundKey, SharedPtr<Sound> > SoundMap;
72  typedef std::map<SharedPtr<Sound>, SoundKey > ReverseSoundMap;
73 
76 
77  struct {
78  int meterDistance : 1;
79  int dopplerFactor : 1;
80  } dirty;
81 
82 
83  SharedPtr<Sound> lookupSound(VSFileSystem::VSFileType type, const std::string &name) const
84  {
85  SoundKey key(type,name);
86  SoundMap::const_iterator it = loadedSounds.find(key);
87  if (it != loadedSounds.end())
88  return it->second;
89  else
90  return SharedPtr<Sound>();
91  }
92 
93  SoundKey lookupSound(const SharedPtr<Sound> &sound) const
94  {
95  ReverseSoundMap::const_iterator it = loadedSoundsReverse.find(sound);
96  if (it != loadedSoundsReverse.end())
97  return it->second;
98  else
99  return SoundKey();
100  }
101 
102  void addSound(VSFileSystem::VSFileType type, const std::string &name, SharedPtr<Sound> sound)
103  {
104  SoundKey key(type,name);
105  loadedSounds[key] = sound;
106  loadedSoundsReverse[sound] = key;
107  }
108 
109  void unloadSound(const SharedPtr<Sound> &sound)
110  {
111  SoundKey key = lookupSound(sound);
112  if (!key.isNull()) {
113  loadedSounds.erase(key);
114  loadedSoundsReverse.erase(sound);
115  }
116  }
117 
119  {
120  for (SoundMap::iterator it=loadedSounds.begin(); it != loadedSounds.end(); ++it)
121  it->second->unload();
122  }
123 
124  void openDevice(const char *deviceSpecifier)
125  throw (Exception)
126  {
127  if (alDevice)
128  throw Exception("Trying to open a device without closing the previous one first");
129 
130  clearAlError();
131 
132  if (deviceSpecifier == NULL) {
133  #ifdef _WIN32
134  deviceSpecifier = "DirectSound3D";
135  #else
136  #ifdef __APPLE__
137  deviceSpecifier = "sdl";
138  #endif
139  #endif
140  }
141 
142  alDevice = alcOpenDevice((ALCstring)(deviceSpecifier));
143 
144  if (!alDevice)
145  checkAlError();
146  else
147  clearAlError();
148  }
149 
150  void closeDevice()
151  throw (Exception)
152  {
153  if (alContext)
154  throw Exception("Trying to close device without closing the previous one first");
155  if (alDevice) {
156  unloadSounds();
157  alcCloseDevice(alDevice);
158  alDevice = NULL;
159  clearAlError();
160  }
161  }
162 
163  void openContext(const Format &format)
164  throw (Exception)
165  {
166  if (alContext)
167  throw Exception("Trying to open context without closing the previous one first");
168  if (!alDevice)
169  throw Exception("Trying to open context without opening a device first");
170 
171  clearAlError();
172 
173  ALCint params[] = {
174  ALC_FREQUENCY, format.sampleFrequency,
175  0
176  };
177 
178  alContext = alcCreateContext(alDevice, params);
179  if (!alContext)
180  checkAlError();
181  else
182  clearAlError();
183 
184  alcMakeContextCurrent(alContext);
185  checkAlError();
186  }
187 
188  void commit()
189  throw (Exception)
190  {
191  alcProcessContext(alContext);
192  checkAlError();
193  }
194 
195  void suspend()
196  throw (Exception)
197  {
198  // FIXME: There's a residual error here on Windows. Can't track down where it's from.
199  alGetError();
200  checkAlError();
201  alcMakeContextCurrent(alContext);
202  alcSuspendContext(alContext);
203  checkAlError();
204  }
205 
207  throw (Exception)
208  {
209  if (alContext) {
210  alcMakeContextCurrent(NULL);
211  alcDestroyContext(alContext);
212  clearAlError();
213  alContext = NULL;
214  }
215  }
216 
217  RendererData() throw() :
218  alDevice(NULL),
219  alContext(NULL)
220  {
221  }
222 
224  {
225  unloadSounds();
226  closeContext();
227  closeDevice();
228  }
229  };
230  };
231  };
232 
233  using namespace __impl::OpenAL;
234 
236  throw(Exception) :
237  data(new RendererData)
238  {
239  }
240 
242  {
243  }
244 
245  SharedPtr<Sound> OpenALRenderer::getSound(
246  const std::string &name,
248  bool streaming)
249  throw(Exception)
250  {
251  checkContext();
252  SharedPtr<Sound> sound = data->lookupSound(type,name);
253  if (!sound.get() || streaming) {
254  if (streaming) {
255  // Streaming sounds cannot be cached, so if a streaming sound
256  // is in the cache, it must be evicted and re-created
257  sound.reset();
258  data->addSound(
259  type,
260  name,
261  sound = SharedPtr<Sound>(new OpenALStreamingSound(name,type))
262  );
263  } else {
264  data->addSound(
265  type,
266  name,
267  sound = SharedPtr<Sound>(new OpenALSimpleSound(name,type))
268  );
269  }
270  }
271  return sound;
272  }
273 
274  bool OpenALRenderer::owns(SharedPtr<Sound> sound)
275  {
276  return !data->lookupSound(sound).isNull();
277  }
278 
279  void OpenALRenderer::attach(SharedPtr<Source> source)
280  throw(Exception)
281  {
282  checkContext();
283  source->setRenderable( SharedPtr<RenderableSource>(
284  source->getSound()->isStreaming()
286  : (RenderableSource*)new OpenALRenderableSource(source.get())
287  )
288  );
289  }
290 
291  void OpenALRenderer::attach(SharedPtr<Listener> listener)
292  throw(Exception)
293  {
294  checkContext();
295  listener->setRenderable( SharedPtr<RenderableListener>(
296  new OpenALRenderableListener(listener.get()) ) );
297  }
298 
299  void OpenALRenderer::detach(SharedPtr<Source> source)
300  throw()
301  {
302  // Just clear it... RenderableListener's destructor will handle everything fine.
303  source->setRenderable( SharedPtr<RenderableSource>() );
304  }
305 
306  void OpenALRenderer::detach(SharedPtr<Listener> listener)
307  throw()
308  {
309  // Just clear it... RenderableListener's destructor will handle everything fine.
310  listener->setRenderable( SharedPtr<RenderableListener>() );
311  }
312 
314  throw()
315  {
316  // ToDo
317  // Nothing yet - this is an extension to OpenAL 1.1's specs and in this phase
318  // we'll implement only basic functionality.
319  Renderer::setMeterDistance(distance);
320 
321  // meterDistance affects doppler settings (since it affects the speed of sound)
322  data->dirty.dopplerFactor = 1;
323  data->dirty.meterDistance = 1;
324  }
325 
327  throw()
328  {
330 
331  // Just flag it as dirty so that the next commit reconfigures the doppler effect.
332  data->dirty.dopplerFactor = 1;
333  }
334 
336  throw(Exception)
337  {
338  if (!data->alDevice)
339  data->openDevice(NULL);
340  data->closeContext();
341  data->openContext(format);
343  }
344 
346  throw(Exception)
347  {
348  if (!data->alDevice)
349  data->openDevice(NULL);
350  if (!data->alContext) {
351  data->openContext(getOutputFormat());
352  initContext();
353  }
354  }
355 
357  throw(Exception)
358  {
359  data->suspend();
360 
361  if (data->dirty.dopplerFactor)
362  setupDopplerEffect();
363  }
364 
366  throw(Exception)
367  {
368  data->commit();
369  }
370 
372  throw(Exception)
373  {
374  // Set the distance model
375  alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);
376 
377  // Flag everything as dirty
378  data->dirty.meterDistance = 1;
379  data->dirty.dopplerFactor = 1;
380  }
381 
383  throw(Exception)
384  {
385  clearAlError();
386 
387  // First of all, compute the speed of sound (in world units)
388  Scalar speedOfSound = 343.3f * getMeterDistance();
389 
390  // Set doppler factor and speed of sound
391  alDopplerFactor(getDopplerFactor());
392 #ifdef _WIN32
393  alDopplerVelocity(speedOfSound);
394 #else
395  alSpeedOfSound(speedOfSound);
396 #endif
397 
398  data->dirty.dopplerFactor = 0;
399 
400  checkAlError();
401  }
402 
403  BorrowedOpenALRenderer::BorrowedOpenALRenderer(ALCdevice *device, ALCcontext *context)
404  throw(Exception) :
406  {
407  if (device)
408  data->alDevice = device;
409  if (context)
410  data->alContext = context;
411  else
412  data->alContext = alcGetCurrentContext();
413  if (!device && data->alContext)
414  data->alDevice = alcGetContextsDevice(data->alContext);
415 
416  initContext();
417  }
418 
420  {
421  data->alDevice = NULL;
422  data->alContext = NULL;
423  }
424 
426  throw(Exception)
427  {
428  // No-op... format is given by the borrowed context
430  }
431 
433  throw(Exception)
434  {
435  // No-op... context has been borrowed
436  }
437 
438 
439 };