audio3d.cpp

Go to the documentation of this file.
00001 /*
00002  * audio3d.cpp
00003  *
00004  * Copyright (C) 2009  Thomas A. Vaughan
00005  * All rights reserved.
00006  *
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted provided that the following conditions are met:
00010  *     * Redistributions of source code must retain the above copyright
00011  *       notice, this list of conditions and the following disclaimer.
00012  *     * Redistributions in binary form must reproduce the above copyright
00013  *       notice, this list of conditions and the following disclaimer in the
00014  *       documentation and/or other materials provided with the distribution.
00015  *     * Neither the name of the <organization> nor the
00016  *       names of its contributors may be used to endorse or promote products
00017  *       derived from this software without specific prior written permission.
00018  *
00019  * THIS SOFTWARE IS PROVIDED BY THOMAS A. VAUGHAN ''AS IS'' AND ANY
00020  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00021  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00022  * DISCLAIMED. IN NO EVENT SHALL THOMAS A. VAUGHAN BE LIABLE FOR ANY
00023  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00024  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00025  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00026  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00027  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00028  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029  *
00030  *
00031  * Implementation of a basic 3D audio library.  See audio3d.h
00032  */
00033 
00034 // includes --------------------------------------------------------------------
00035 #include "audio3d.h"            // always include our own header first
00036 
00037 #include "common/wave_ex.h"
00038 #include "perf/perf.h"
00039 
00040 
00041 namespace audio3d {
00042 
00043 
00044 // interface destructor implementations
00045 SoundManager::~SoundManager(void) throw() { }
00046 
00047 static const float s_minR2              = 0.05;
00048 
00049 
00050 ////////////////////////////////////////////////////////////////////////////////
00051 //
00052 //      static helper methods
00053 //
00054 ////////////////////////////////////////////////////////////////////////////////
00055 
00056 static  void
00057 getVolumeAndPan
00058 (
00059 IN const point3d_t& r,          // vector from sound to listener
00060 IN const point3d_t& right,      // which way is to listener's right
00061 OUT float& volume,              // volume of sound
00062 OUT float& pan                  // pan
00063 )
00064 throw()
00065 {
00066         point3d_t n = r;
00067         normalize(n);
00068         pan = dotProduct(n, right);
00069         float R2 = dotProduct(r, r);
00070         if (R2 < s_minR2)
00071                 R2 = s_minR2;
00072         volume = 1.0 / R2;      // volume is in range 0-20
00073 }
00074 
00075 
00076 
00077 ////////////////////////////////////////////////////////////////////////////////
00078 //
00079 //      SndMgr -- class that implements the audio3d::SoundManager interface
00080 //
00081 ////////////////////////////////////////////////////////////////////////////////
00082 
00083 class SndMgr : public SoundManager {
00084 public:
00085         // constructor, destructor ---------------------------------------------
00086         SndMgr(void) throw(); 
00087         ~SndMgr(void) throw() { }
00088 
00089         // public class methods ------------------------------------------------
00090         void initialize(IN const char * device_name,
00091                                 IN const char * parameters);
00092 
00093         // audio3d::SoundManager class interface methods -----------------------
00094         id_t createListener(void);
00095         void updateListener(IN id_t listenerId,
00096                                 IN const point3d_t& position,
00097                                 IN const point3d_t& facing,
00098                                 IN const point3d_t& up);
00099         bool removeListener(IN id_t listenerId);
00100 
00101         id_t createSound(IN id_t listenerId,
00102                                 IN const char * filename,
00103                                 IN float baseVolume);
00104         void updateSound(IN id_t soundId,
00105                                 IN const point3d_t& position);
00106         bool removeSound(IN id_t soundId);
00107 
00108         void updateSounds(void);
00109 
00110 private:
00111         // private typedefs ----------------------------------------------------
00112         struct sound_t {
00113                 // data fields
00114                 point3d_t               position;
00115                 audiere::OutputStreamPtr        sound;
00116                 float                   baseVolume;
00117                 id_t                    listenerId;
00118         };
00119 
00120         typedef std::map<id_t, smart_ptr<sound_t> > sound_map_t;
00121 
00122         struct listener_t {
00123                 // data fields
00124                 point3d_t       position;
00125                 point3d_t       right;
00126                 sound_map_t     sounds;
00127         };
00128 
00129         typedef std::map<id_t, smart_ptr<listener_t> > listener_map_t;
00130 
00131         // private helper methods ----------------------------------------------
00132         id_t getNextId(void);
00133         listener_t * getListener(IN id_t id);
00134         sound_t * getSound(IN id_t id);
00135 
00136         // private member data -------------------------------------------------
00137         audiere::AudioDevicePtr         m_device;
00138         id_t                            m_objCtr;
00139         listener_map_t                  m_listeners;
00140         sound_map_t                     m_sounds;       // global map
00141 };
00142 
00143 
00144 
00145 SndMgr::SndMgr(void)
00146 throw()
00147 {
00148         m_objCtr = 1;
00149 }
00150 
00151 
00152 
00153 ////////////////////////////////////////////////////////////////////////////////
00154 //
00155 //      SndMgr -- public class methods
00156 //
00157 ////////////////////////////////////////////////////////////////////////////////
00158 
00159 void
00160 SndMgr::initialize
00161 (
00162 IN const char * device_name,
00163 IN const char * parameters
00164 )
00165 {
00166         // ASSERT(device_name) -- can be null!
00167         // ASSERT(parameters) -- can be null!
00168 
00169         // create a sound device
00170         m_device = audiere::OpenDevice(device_name, parameters);
00171         if (!m_device) {
00172                 WAVE_EX(wex);
00173                 wex << "Failed to open audio device";
00174         }
00175 }
00176 
00177 
00178 
00179 ////////////////////////////////////////////////////////////////////////////////
00180 //
00181 //      SndMgr -- audio3d::SoundManager class interface methods
00182 //
00183 ////////////////////////////////////////////////////////////////////////////////
00184 
00185 id_t
00186 SndMgr::createListener
00187 (
00188 void
00189 )
00190 {
00191         id_t id = this->getNextId();
00192 
00193         smart_ptr<listener_t> l = new listener_t;
00194         ASSERT(l, "out of memory");
00195 
00196         // set defaults
00197         l->position     = point3d_t(0, 0, 0);   // origin
00198         l->right        = point3d_t(1, 0, 0);
00199 
00200         m_listeners[id] = l;
00201 
00202         return id;
00203 }
00204 
00205 
00206 
00207 void
00208 SndMgr::updateListener
00209 (
00210 IN id_t listenerId,
00211 IN const point3d_t& position,
00212 IN const point3d_t& facing,
00213 IN const point3d_t& up
00214 )
00215 {
00216         ASSERT(listenerId, "Bad listener id: %d", (int) listenerId);
00217 
00218         listener_t * l = this->getListener(listenerId);
00219         ASSERT(l, "null");
00220 
00221         l->position = position;
00222         l->right = crossProduct(facing, up);
00223 }
00224 
00225 
00226 
00227 bool
00228 SndMgr::removeListener
00229 (
00230 IN id_t listenerId
00231 )
00232 {
00233         ASSERT(listenerId, "Bad listener id: %d", (int) listenerId);
00234 
00235         listener_map_t::iterator i = m_listeners.find(listenerId);
00236         if (m_listeners.end() == i) {
00237                 DPRINTF("Listener not found? %d", (int) listenerId);
00238                 return false;
00239         }
00240         m_listeners.erase(i);
00241         return true;
00242 }
00243 
00244 
00245 
00246 id_t
00247 SndMgr::createSound
00248 (
00249 IN id_t listenerId,
00250 IN const char * filename,
00251 IN float baseVolume             ///< volume at 1m distance
00252 )
00253 {
00254         ASSERT(listenerId, "bad listener id: %d", (int) listenerId);
00255         ASSERT(filename, "null");
00256         ASSERT(baseVolume >= 0.0 && baseVolume <= 1.0,
00257             "bad base volume: %f", baseVolume);
00258 
00259         // verify we have this listener ID
00260         listener_t * l = this->getListener(listenerId);
00261         ASSERT(l, "null");
00262 
00263         // create new sound record
00264         smart_ptr<sound_t> sound = new sound_t;
00265         ASSERT(sound, "out of memory");
00266 
00267         sound->sound = audiere::OpenSound(m_device, filename);
00268         if (!sound->sound) {
00269                 WAVE_EX(wex);
00270                 wex << "Failed to create sound from file: " << filename;
00271         }
00272         sound->listenerId = listenerId;
00273         sound->baseVolume = baseVolume;
00274 
00275         // add sound to listener and global map
00276         id_t id = this->getNextId();
00277         l->sounds[id] = sound;
00278         m_sounds[id] = sound;
00279         ASSERT(3 == sound.get_ref_count(), "bad ref count!");
00280 
00281         return id;
00282 }
00283 
00284 
00285 
00286 void
00287 SndMgr::updateSound
00288 (
00289 IN id_t soundId,
00290 IN const point3d_t& position
00291 )
00292 {
00293         ASSERT(soundId, "Bad sound id: %d", (int) soundId);
00294 
00295         sound_t * sound = this->getSound(soundId);
00296         ASSERT(sound, "null");
00297 
00298         sound->position = position;
00299 }
00300 
00301 
00302 
00303 bool
00304 SndMgr::removeSound
00305 (
00306 IN id_t soundId
00307 )
00308 {
00309         ASSERT(soundId, "Bad sound id: %d", (int) soundId);
00310 
00311         bool retval = false;    // assume not found
00312 
00313         // look up
00314         sound_t * sound = this->getSound(soundId);
00315 
00316         // remove from listener map
00317         listener_t * l = this->getListener(sound->listenerId);
00318         ASSERT(l, "null");
00319         sound_map_t::iterator i = l->sounds.find(soundId);
00320         if (l->sounds.end() != i) {
00321                 l->sounds.erase(i);
00322         }
00323 
00324         // remove from global map
00325         i = m_sounds.find(soundId);
00326         if (m_sounds.end() != i) {
00327                 m_sounds.erase(i);
00328                 retval = true;
00329         }
00330 
00331         return retval;
00332 }
00333 
00334 
00335 
00336 void
00337 SndMgr::updateSounds
00338 (
00339 void
00340 )
00341 {
00342         // loop through all listeners and adjust output accordingly
00343         for (listener_map_t::iterator i = m_listeners.begin();
00344              i != m_listeners.end(); ++i) {
00345 
00346                 listener_t * l = i->second;
00347                 ASSERT(l, "null listener in map?");
00348 
00349                 // loop through all sounds for this listener
00350                 for (sound_map_t::iterator j = l->sounds.begin();
00351                      j != l->sounds.end(); ++j) {
00352                         sound_t * sound = j->second;
00353                         ASSERT(sound, "null sound in space map?");
00354                         ASSERT(sound->sound, "null");
00355 
00356                         // get volume and panning for relative position
00357                         float volume, pan;
00358                         getVolumeAndPan(sound->position - l->position,
00359                                 l->right, volume, pan);
00360                         ASSERT(volume >= 0 && volume <= 1, "Bad volume: %f",
00361                             volume);
00362                         ASSERT(pan >= -1 && pan <= 1, "Bad pan: %f", pan);
00363 
00364                         volume *= sound->baseVolume;
00365                         if (volume > 1.0) {
00366                                 volume = 1.0;   // cap volume
00367                         }
00368 
00369                         // update sound
00370                         sound->sound->setVolume(volume);
00371                         sound->sound->setPan(pan);
00372 
00373                         // start playing if not already!
00374                         if (!sound->sound->isPlaying())
00375                                 sound->sound->play();
00376                 }
00377         }
00378 }
00379 
00380 
00381 
00382 ////////////////////////////////////////////////////////////////////////////////
00383 //
00384 //      SndMgr -- private helper methods
00385 //
00386 ////////////////////////////////////////////////////////////////////////////////
00387 
00388 id_t
00389 SndMgr::getNextId
00390 (
00391 void
00392 )
00393 {
00394         id_t id = m_objCtr;
00395         m_objCtr++;     // not worried about wrapping
00396         return id;
00397 }
00398 
00399 
00400 
00401 SndMgr::listener_t *
00402 SndMgr::getListener
00403 (
00404 IN id_t id
00405 )
00406 {
00407         ASSERT(id, "null");
00408 
00409         listener_map_t::iterator i = m_listeners.find(id);
00410         ASSERT(m_listeners.end() != i, "No such listener: %d", (int) id);
00411         return i->second;
00412 }
00413 
00414 
00415 
00416 SndMgr::sound_t *
00417 SndMgr::getSound
00418 (
00419 IN id_t id
00420 )
00421 {
00422         ASSERT(id, "null");
00423 
00424         sound_map_t::iterator i = m_sounds.find(id);
00425         ASSERT(m_sounds.end() != i, "no such sound: %d", (int) id);
00426 
00427         return i->second;
00428 }
00429 
00430 
00431 
00432 ////////////////////////////////////////////////////////////////////////////////
00433 //
00434 //      public API
00435 //
00436 ////////////////////////////////////////////////////////////////////////////////
00437 
00438 smart_ptr<SoundManager>
00439 SoundManager::create
00440 (
00441 IN const char * device_name,    ///< device name passed into audiere::OpenDevice
00442 IN const char * parameters      ///< parameters passed into audiere::OpenDevice
00443 )
00444 {
00445         // ASSERT(device_name) -- can be null!
00446         // ASSERT(parameters) -- can be null!
00447 
00448         smart_ptr<SndMgr> local = new SndMgr;
00449         ASSERT(local, "out of memory");
00450 
00451         local->initialize(device_name, parameters);
00452 
00453         return local;
00454 }
00455 
00456 
00457 
00458 };      // audio3d namespace
00459