Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/trunk/src/lib/sound/sound_engine.cc @ 8190

Last change on this file since 8190 was 7729, checked in by bensch, 18 years ago

orxonox/trunk: better debug names
As DEBUG and ERROR are already given to windows.h and other files:
DEBUG is renamed to DEBUG_LEVEL
and all
NO, ERR, WARN, INFO, DEBUG, vDEBUG
are renamed to
ORX_NONE, ORX_ERR, ORX_WARN, ORX_INFO, ORX_DEBUG, ORX_vDEBUG

File size: 12.2 KB
Line 
1/*
2   orxonox - the future of 3D-vertical-scrollers
3
4   Copyright (C) 2004 orx
5
6   This program 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, or (at your option)
9   any later version.
10
11   ### File Specific:
12   main-programmer: Benjamin Grauer
13   co-programmer: ...
14
15   code has been taken from http://www.devmaster.net/articles.php?catID=6
16   The code has been applied to our needs, and many things have been changed.
17*/
18
19#define DEBUG_SPECIAL_MODULE DEBUG_MODULE_SOUND
20
21#include "sound_engine.h"
22
23#include "class_list.h"
24
25#include "p_node.h"
26#include "util/loading/resource_manager.h"
27#include "debug.h"
28#include "util/preferences.h"
29#include "globals.h"
30
31namespace OrxSound
32{
33
34  //////////////////
35  /* SOUND-ENGINE */
36  //////////////////
37  /**
38   * @brief standard constructor
39  */
40  SoundEngine::SoundEngine ()
41  {
42    this->setClassID(CL_SOUND_ENGINE, "SoundEngine");
43    this->setName("SoundEngine");
44
45    this->listener = NULL;
46    this->bufferList = NULL;
47    this->sourceList = NULL;
48
49    this->device = NULL;
50    this->context = NULL;
51
52    this->maxSourceCount = 32;
53
54    this->effectsVolume = .80;
55    this->musicVolume = .75;
56
57    this->sourceMutex = SDL_CreateMutex();
58  }
59
60  /**
61   * @brief the singleton reference to this class
62  */
63  SoundEngine* SoundEngine::singletonRef = NULL;
64
65  /**
66   * @brief standard destructor
67   */
68  SoundEngine::~SoundEngine ()
69  {
70    // deleting all the SoundSources
71    if(this->sourceList != NULL)
72    {
73      while (this->sourceList->size() > 0)
74        delete static_cast<SoundSource*>(this->sourceList->front());
75    }
76
77    while(!this->ALSources.empty())
78    {
79      if (alIsSource(this->ALSources.top()))
80      {
81        alDeleteSources(1, &this->ALSources.top());
82        SoundEngine::checkError("Deleting Source", __LINE__);
83      }
84      else
85        PRINTF(1)("%d is not a Source\n", this->ALSources.top());
86
87      this->ALSources.pop();
88    }
89
90    // deleting all the SoundBuffers
91    if (this->bufferList != NULL)
92    {
93      while(this->bufferList->size() > 0)
94        ResourceManager::getInstance()->unload(static_cast<SoundBuffer*>(this->bufferList->front()));
95    }
96
97    // removing openAL from AudioResource
98    //! @todo this should be terminated through alc
99    //alutExit();
100
101    SDL_DestroyMutex(this->sourceMutex);
102
103    SoundEngine::singletonRef = NULL;
104  }
105
106  /**
107   * @brief loads the settings of the SoundEngine from an ini-file
108   */
109  void SoundEngine::loadSettings()
110  {
111    MultiType channels = Preferences::getInstance()->getString(CONFIG_SECTION_AUDIO, CONFIG_NAME_AUDIO_CHANNELS, "32");
112    this->maxSourceCount = channels.getInt();
113
114    MultiType effectsVolume = Preferences::getInstance()->getString(CONFIG_SECTION_AUDIO, CONFIG_NAME_EFFECTS_VOLUME, "80");
115    this->effectsVolume = effectsVolume.getFloat()/100.0;
116
117    MultiType musicVolume = Preferences::getInstance()->getString(CONFIG_SECTION_AUDIO, CONFIG_NAME_MUSIC_VOLUME, "75");
118    this->musicVolume = musicVolume.getFloat()/100.0;
119  }
120
121  /**
122   * @brief creates a new SoundSource.
123   * @param fileName The Name to load the SoundBuffer from
124   * @param sourceNode The sourceNode to bind this SoundSource to.
125   * @returns The newly created SoundSource
126   *
127   * acctualy this is nothing more than a wrapper around the ResourceManager.
128  */
129  SoundSource* SoundEngine::createSource(const std::string& fileName, PNode* sourceNode)
130  {
131    SoundBuffer* buffer = NULL;
132    if (!fileName.empty())
133    {
134      buffer= (SoundBuffer*)ResourceManager::getInstance()->load(fileName, WAV, RP_LEVEL);
135      if (buffer == NULL)
136        PRINTF(2)("Wav-Sound %s could not be loaded onto new Source\n", fileName.c_str());
137    }
138    return new SoundSource(sourceNode, buffer);
139  }
140
141  /**
142   * @brief Sets the doppler values of openAL
143   * @param dopplerFactor the extent of the doppler-effect
144   * @param dopplerVelocity the Speed the sound travels
145  */
146  void SoundEngine::setDopplerValues(ALfloat dopplerFactor, ALfloat dopplerVelocity)
147  {
148    alDopplerFactor(dopplerFactor);
149    this->checkError("Setting Doppler Factor", __LINE__);
150
151    alDopplerVelocity(dopplerVelocity);
152    this->checkError("Setting Doppler Velocity", __LINE__);
153  }
154
155
156  /**
157   * @brief retrieves an OpenAL Source from the availiable Sources.
158   * @param source the Source to fill with the Value.
159   */
160  void SoundEngine::popALSource(ALuint& source)
161  {
162    assert (source == 0);
163    /// @TODO try to create more sources if needed
164    if (!this->ALSources.empty())
165    {
166      SDL_mutexP(this->sourceMutex);
167      source = this->ALSources.top();
168      this->ALSources.pop();
169      SDL_mutexV(this->sourceMutex);
170    }
171  }
172
173
174  /**
175   * @brief Pushes an OpenAL Source back into the Stack of known sources
176   * @param source the Source to push onto the top of the SourceStack
177   */
178  void SoundEngine::pushALSource(ALuint& source)
179  {
180    if (source != 0)
181    {
182      SDL_mutexP(this->sourceMutex);
183      this->ALSources.push(source);
184      SDL_mutexV(this->sourceMutex);
185    }
186  };
187
188
189  /**
190   * @brief updates all The positions, Directions and Velocities of all Sounds
191   */
192  void SoundEngine::update()
193  {
194    // updating the Listeners Position
195    if (likely(this->listener != NULL))
196    {
197      alListener3f(AL_POSITION,
198                   this->listener->getAbsCoor().x,
199                   this->listener->getAbsCoor().y,
200                   this->listener->getAbsCoor().z);
201      alListener3f(AL_VELOCITY,
202                   this->listener->getVelocity().x,
203                   this->listener->getVelocity().y,
204                   this->listener->getVelocity().z);
205      Vector absDirV = this->listener->getAbsDirV();
206      ALfloat orientation [6] = {1,0,0, absDirV.x, absDirV.y, absDirV.z};
207      alListenerfv(AL_ORIENTATION, orientation);
208      SoundEngine::checkError("SoundEngine::update() - Listener Error", __LINE__);
209    }
210    else
211      PRINTF(2)("no listener defined\n");
212
213    // updating all the Sources positions
214    if (likely(this->sourceList != NULL || (this->sourceList = ClassList::getList(CL_SOUND_SOURCE)) != NULL))
215    {
216      std::list<BaseObject*>::const_iterator sourceIT;
217      SoundSource* source;
218      for (sourceIT = this->sourceList->begin(); sourceIT != this->sourceList->end(); sourceIT++)
219      {
220        source = static_cast<SoundSource*>(*sourceIT);
221        if (source->isPlaying())
222        {
223          int play = 0x000;
224          alGetSourcei(source->getID(), AL_SOURCE_STATE, &play);
225          if (DEBUG_LEVEL > 2)
226            SoundEngine::checkError("SoundEngine::update() Play", __LINE__);
227          if(play == AL_PLAYING)
228          {
229            if (likely(source->getNode() != NULL))
230            {
231              alSource3f(source->getID(), AL_POSITION,
232                         source->getNode()->getAbsCoor().x,
233                         source->getNode()->getAbsCoor().y,
234                         source->getNode()->getAbsCoor().z);
235              if (DEBUG_LEVEL > 2)
236                SoundEngine::checkError("SoundEngine::update() Set Source Position", __LINE__);
237              alSource3f(source->getID(), AL_VELOCITY,
238                         source->getNode()->getVelocity().x,
239                         source->getNode()->getVelocity().y,
240                         source->getNode()->getVelocity().z);
241              if (DEBUG_LEVEL > 2)
242                SoundEngine::checkError("SoundEngine::update() Set Source Velocity", __LINE__);
243            }
244          }
245          else
246          {
247            source->stop();
248          }
249        }
250      }
251    }
252    SoundEngine::checkError("SoundEngine::update()", __LINE__);
253  }
254
255
256  /**
257   *  initializes Audio in general
258  */
259  bool SoundEngine::initAudio()
260  {
261    //   ALenum result;
262    //   PRINTF(3)("Initialisazing openAL sound engine\n");
263    //   const char* defaultDevice =(const char*) alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
264    //   const char* deviceList = (const char*)alcGetString(NULL,ALC_DEVICE_SPECIFIER);
265    //   const char* devWalk = deviceList;
266    //   //  if (alcIsExtensionPresent(NULL, (const ALCchar*)"ALC_ENUMERATION_EXT") == AL_TRUE)
267    //   { // try out enumeration extension
268    //     PRINTF(3)("Enumeration-extension found\n");
269    //
270    //     PRINTF(3)("Default device: %s\n", defaultDevice);
271    //     do
272    //     {
273    //       PRINTF(3)("%s\n", devWalk);
274    //       devWalk += strlen(devWalk)+1;
275    //     }
276    //     while (devWalk[0] != '\0');
277    //  }
278
279    // INITIALIZING THE DEVICE:
280    //   char deviceName[] =
281    //   #ifdef __WIN32__
282    //     "Direct3D";
283    //   #else
284    //     "'( ( devices '( native null ) ) )";
285    //   #endif
286
287    this->device = alcOpenDevice(NULL);
288    this->checkALCError("opening Device", __LINE__);
289
290    PRINTF(4)("Audio-Specifier: %s\n", (const char*)alcGetString(this->device, ALC_DEVICE_SPECIFIER));
291    PRINTF(4)("Audio-Extensions: %s\n", (const char*)alcGetString(this->device, ALC_EXTENSIONS));
292
293
294    this->context = alcCreateContext(this->device, NULL);
295    this->checkALCError("creating Context", __LINE__);
296
297    alcMakeContextCurrent(this->context);
298    this->checkALCError("making Context Current", __LINE__);
299    // #endif
300
301    this->setDopplerValues(SOUND_DOPPLER_FACTOR, SOUND_DOPPLER_VELOCITY);
302    this->allocateSources(this->maxSourceCount);
303    this->checkError("Allocating Sources", __LINE__);
304  }
305
306
307  /**
308   * Allocates openAL sources
309   * @param count how many sources to allocate
310   * @returns true on success, false if at least one source could not be allocated
311   */
312  bool SoundEngine::allocateSources(unsigned int count)
313  {
314    unsigned int failCount = 0;
315    for (unsigned int i = 0; i < count; i++)
316    {
317      ALuint source = 0;
318
319      alGenSources(1, &source);
320      this->checkError("allocate Source", __LINE__);
321      if (!alIsSource(source))
322      {
323        PRINTF(5)("not allocated Source\n");
324        failCount++;
325        continue;
326      }
327
328      alSourcef (source, AL_PITCH,    1.0      );
329      alSourcef (source, AL_GAIN,     this->getEffectsVolume() );
330      alSourcei (source, AL_LOOPING,  AL_FALSE );
331      this->ALSources.push(source);
332    }
333    if (failCount == 0)
334      return true;
335    else
336    {
337      PRINTF(2)("Failed to allocate %d of %d SoundSources\n", failCount, count);
338    }
339  }
340
341  /**
342   * @brief checks for an OpenAL error
343   * @param error the ErrorMessage to display
344   * @param line on what line did the error occure.
345   */
346  bool SoundEngine::checkError(const std::string& error, unsigned int line)
347  {
348    ALenum errorCode;
349    if ((errorCode = alGetError()) != AL_NO_ERROR)
350    {
351      PRINTF(1)("Error %s (line:%d): '%s'\n", error.c_str(), line, SoundEngine::getALErrorString(errorCode));
352      return false;
353    }
354    else
355      return true;
356  }
357
358  /**
359   * @brief check for an ALC error.
360   * @brief error the Error-String to display
361   * @param line on that line, the error occured (debugging mode).
362   */
363  bool SoundEngine::checkALCError(const std::string& error, unsigned int line)
364  {
365    ALenum errorCode;
366    if ((errorCode = alcGetError(this->device)) != ALC_NO_ERROR)
367    {
368      PRINTF(1)("Error %s (line:%d): '%s'\n", error.c_str(), line, SoundEngine::getALCErrorString(errorCode));
369      return false;
370    }
371    else
372      return true;
373  }
374
375
376
377  void SoundEngine::listDevices()
378  {
379    printf("%s\n",(const char*)alcGetString(NULL, ALC_DEVICE_SPECIFIER));
380  }
381
382  /**
383   *  Transforms AL-errors into something readable
384   * @param err The error found
385  */
386  const char* SoundEngine::getALErrorString(ALenum err)
387  {
388    switch(err)
389    {
390    case AL_NO_ERROR:
391      return ("AL_NO_ERROR");
392    case AL_INVALID_NAME:
393      return ("AL_INVALID_NAME");
394    case AL_INVALID_ENUM:
395      return ("AL_INVALID_ENUM");
396    case AL_INVALID_VALUE:
397      return ("AL_INVALID_VALUE");
398    case AL_INVALID_OPERATION:
399      return ("AL_INVALID_OPERATION");
400    case AL_OUT_OF_MEMORY:
401      return ("AL_OUT_OF_MEMORY");
402    };
403  }
404
405
406  const char* SoundEngine::getALCErrorString(ALenum err)
407  {
408    switch(err)
409    {
410    case ALC_NO_ERROR:
411      return ("AL_NO_ERROR");
412    case ALC_INVALID_DEVICE:
413      return ("ALC_INVALID_DEVICE");
414    case ALC_INVALID_CONTEXT:
415      return("ALC_INVALID_CONTEXT");
416    case ALC_INVALID_ENUM:
417      return("ALC_INVALID_ENUM");
418    case ALC_INVALID_VALUE:
419      return ("ALC_INVALID_VALUE");
420    case ALC_OUT_OF_MEMORY:
421      return("ALC_OUT_OF_MEMORY");
422    };
423  }
424}
Note: See TracBrowser for help on using the repository browser.