lugre_sound_openal2.cpp

Go to the documentation of this file.
00001 /*
00002  * Alternative OpenAL sound module for Lugre
00003  * Copyright (C) 2007 Unavowed <unavowed at vexillium org>
00004  * 
00005  * Permission is hereby granted, free of charge, to any person obtaining a
00006  * copy of this software and associated documentation files (the "Software"),
00007  * to deal in the Software without restriction, including without limitation
00008  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
00009  * and/or sell copies of the Software, and to permit persons to whom the
00010  * Software is furnished to do so, subject to the following conditions:
00011  * 
00012  * The above copyright notice and this permission notice shall be included in
00013  * all copies or substantial portions of the Software.
00014  * 
00015  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00016  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00017  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
00018  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00019  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
00020  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
00021  * DEALINGS IN THE SOFTWARE.
00022  */
00023 
00024 #ifdef USE_OPENAL
00025 
00026 #include "lugre_prefix.h"
00027 
00028 #if LUGRE_PLATFORM == LUGRE_PLATFORM_APPLE
00029 #include <OpenAL/al.h>
00030 #include <OpenAL/alc.h>
00031 #else
00032 #include <AL/al.h>
00033 #include <AL/alc.h>
00034 #endif
00035 
00036 
00037 #include <boost/detail/endian.hpp>
00038 #include <boost/thread/xtime.hpp>
00039 
00040 #ifdef ENABLE_THREADS
00041 #  include <boost/thread.hpp>
00042 #endif
00043 
00044 #ifdef WIN32
00045 #include <cstdlib>
00046 #endif
00047 
00048 #include <vorbis/vorbisfile.h>
00049 
00050 #include <algorithm>
00051 #include <cassert>
00052 #include <fstream>
00053 #include <iomanip>
00054 #include <iostream>
00055 #include <limits>
00056 #include <map>
00057 #include <set>
00058 #include <sstream>
00059 #include <stdexcept>
00060 #include <string>
00061 #include <vector>
00062 
00063 #include "lugre_shell.h"
00064 #include "lugre_sound.h"
00065 
00066 /*
00067  * The maximum size, in bytes, of a single stream buffer.
00068  */
00069 #define STREAM_BUFFER_SIZE (32 * 1024)
00070 
00071 /*
00072  * The number of buffers used in a single stream.
00073  */
00074 #define STREAM_BUFFER_COUNT 4
00075 
00076 /*
00077  * The maximum buffer size, in bytes.  If a sound is smaller than this, it is
00078  * completely loaded into memory.  Otherwise it is streamed.
00079  */
00080 #define MAXIMUM_BUFFER_SIZE (256 * 1024)
00081 
00082 /*
00083  * The time, in seconds, after which an unused shared buffer is freed, if
00084  * the number of shared buffer is above IMMORTAL_SHARED_BUFFER_COUNT.
00085  */
00086 #define SHARED_BUFFER_TIMEOUT 120
00087 
00088 /*
00089  * The maximum number of shared buffers that never get freed.
00090  */
00091 #define IMMORTAL_SHARED_BUFFER_COUNT 16
00092 
00093 /*
00094  * Define this to nothing to turn off checking whether OpenAL calls have
00095  * succeeded.
00096  */
00097 #define CHECK_OPENAL \
00098     check_openal (__LINE__)
00099 
00100 #ifdef ENABLE_THREADS
00101 #  define DECLARE_MUTEX \
00102      boost::recursive_mutex mutex
00103 #  define HOLD_LOCK \
00104      boost::recursive_mutex::scoped_lock lock_holder (this->mutex)
00105 #else
00106 #  define DECLARE_MUTEX
00107 #  define HOLD_LOCK
00108 #endif
00109 
00110 #ifndef SIZE_MAX
00111 #  define SIZE_MAX (std::numeric_limits<size_t>::max ())
00112 #endif
00113 
00114 namespace Lugre
00115 {
00116 
00117 /*
00118  * Forward declaration
00119  */
00120 class OpenALSoundSystem;
00121 
00122 
00123 /*
00124  *
00125  * Private
00126  *
00127  */
00128 
00129 namespace
00130 {
00131 
00132 inline void
00133 check_openal (int line)
00134 {
00135   ALenum err;
00136   //std::ostringstream ss;
00137 
00138   err = alGetError ();
00139   if (err == AL_NO_ERROR)
00140     return;
00141 
00142     //std::cout << "OpenAL error at " __FILE__ ":" << line << ": " << std::hex << err << std::endl;
00143   //throw std::runtime_error (ss.str ());
00144 }
00145 
00146 
00147 /*
00148  * A small class encapsulating an OpenAL buffer.
00149  */
00150 class sound_buffer
00151 {
00152 public:
00153   friend class Lugre::OpenALSoundSystem;
00154 
00155 public:
00156   enum type
00157   {
00158     PLAIN,
00159     SHARED
00160   };
00161 
00162 protected:
00163   int channels;
00164 
00165 protected:
00166   virtual ~sound_buffer (void);
00167 
00168 public:
00169   const ALuint name;
00170 
00171   sound_buffer (void);
00172   virtual type get_type (void);
00173   bool set_data (const void *buffer, size_t size, int channels, int bps,
00174          size_t freq);
00175   int get_channel_count (void);
00176 };
00177 
00178 sound_buffer::sound_buffer (void)
00179   : name (0)
00180 {
00181   alGenBuffers (1, (ALuint *) &this->name);
00182   CHECK_OPENAL;
00183 }
00184 
00185 sound_buffer::~sound_buffer (void)
00186 {
00187   alDeleteBuffers (1, &this->name);
00188 }
00189 
00190 sound_buffer::type
00191 sound_buffer::get_type (void)
00192 {
00193   return PLAIN;
00194 }
00195 
00196 bool
00197 sound_buffer::set_data (const void *buffer, size_t size, int channels,
00198             int bps, size_t freq)
00199 {
00200   ALenum format;
00201 
00202   if ((channels != 1 && channels != 2) || (bps != 8 && bps != 16))
00203     {
00204       std::cerr << "OpenAL warning: Unsupported format (" << channels
00205         << " channels, " << bps << " bps)" << std::endl;
00206       return false;
00207     }
00208 
00209   if (channels == 1)
00210     {
00211       if (bps == 8)
00212     format = AL_FORMAT_MONO8;
00213       else
00214     format = AL_FORMAT_MONO16;
00215     }
00216   else if (channels == 2)
00217     {
00218       if (bps == 8)
00219     format = AL_FORMAT_STEREO8;
00220       else
00221     format = AL_FORMAT_STEREO16;
00222     }
00223   else
00224     throw std::logic_error ("Unaccounted for PCM format");
00225 
00226   alBufferData (this->name, format, buffer, size, freq);
00227   CHECK_OPENAL;
00228 
00229   this->channels = channels;
00230   return true;
00231 }
00232 
00233 int
00234 sound_buffer::get_channel_count (void)
00235 {
00236   return this->channels;
00237 }
00238 
00239 
00240 /*
00241  * A reference counted buffer to be shared by more than one source, and
00242  * unloaded after it has not been used for a while.
00243  */
00244 class shared_buffer : public sound_buffer
00245 {
00246 private:
00247   friend class Lugre::OpenALSoundSystem;
00248 
00249 protected:
00250   size_t ref_count;
00251   //boost::xtime last_use_time;
00252   long last_use_time;
00253 
00254 public:
00255   const std::string id;
00256 
00257 public:
00258   shared_buffer (const std::string &ident);
00259   type get_type (void);
00260   void inc_ref (void);
00261   void dec_ref (void);
00262   size_t get_ref_count (void);
00263   //void get_last_use_time (boost::xtime *ret);
00264   long get_last_use_time ();
00265 };
00266 
00267 shared_buffer::shared_buffer (const std::string &ident)
00268   : id (ident)
00269 {
00270   this->ref_count = 1;
00271 }
00272 
00273 sound_buffer::type
00274 shared_buffer::get_type (void)
00275 {
00276   return SHARED;
00277 }
00278 
00279 void
00280 shared_buffer::inc_ref (void)
00281 {
00282   assert (this->ref_count < SIZE_MAX);
00283   this->ref_count += 1;
00284 }
00285 
00286 void
00287 shared_buffer::dec_ref (void)
00288 {
00289   assert (this->ref_count > 0);
00290 
00291   this->ref_count -= 1;
00292   if (this->ref_count == 0)
00293     this->last_use_time = cShell::GetTicks();
00294     //boost::xtime_get (&this->last_use_time, boost::TIME_UTC);
00295 }
00296 
00297 size_t
00298 shared_buffer::get_ref_count (void)
00299 {
00300   return this->ref_count;
00301 }
00302 
00303 long
00304 shared_buffer::get_last_use_time ()
00305 {
00306   if (this->ref_count > 0)
00307     return cShell::GetTicks();
00308     //boost::xtime_get (ret, boost::TIME_UTC);
00309   else
00310     return this->last_use_time;
00311     //*ret = this->last_use_time;
00312 }
00313 
00314 
00315 /*
00316  * Interface for sound file loaders.
00317  */
00318 class sound_stream
00319 {
00320 public:
00321   virtual ~sound_stream (void) { }
00322 
00323   /*
00324    * Returns the total number of PCM frames in the stream or 0 if unknown.
00325    */
00326   virtual size_t get_pcm_size (void) = 0;
00327 
00328   /*
00329    * Loads data into the buffer.  Returns false if no data has been loaded.
00330    * May load less than max_size.
00331    */
00332   virtual bool fill_buffer (sound_buffer *buf,
00333                 size_t max_size = STREAM_BUFFER_SIZE) = 0;
00334 
00335   /*
00336    * Returns true if no more data can be loaded using fill_bufer ()
00337    */
00338   virtual bool is_finished (void) = 0;
00339 
00340   /*
00341    * Returns the channel count.  This is useful to know because at the moment
00342    * OpenAL does not support 3D stereo sources.
00343    */
00344   virtual int get_channel_count (void) = 0;
00345 };
00346 
00347 
00348 /*
00349  *
00350  * Ogg/Vorbis stream
00351  *
00352  */
00353 
00354 /*
00355  * Wrapper around istream::read (), for ov_callbacks.
00356  */
00357 size_t
00358 read_istream (void *ptr, size_t size, size_t nmemb, void *datasource)
00359 {
00360   std::istream *is;
00361 
00362   is = (std::istream *) datasource;
00363   is->read ((char *) ptr, size * nmemb);
00364   is->clear ();
00365 
00366   return (is->gcount () / size);
00367 }
00368 
00369 /*
00370  * Wrapper around istream::seekg (), for ov_callbacks.
00371  */
00372 int
00373 seek_istream (void *datasource, ogg_int64_t offset, int whence)
00374 {
00375   std::ios_base::seekdir wh;
00376   std::istream *is;
00377 
00378   is = (std::istream *) datasource;
00379 
00380   if (whence == SEEK_SET)
00381     wh = std::ios_base::beg;
00382   else if (whence == SEEK_CUR)
00383     wh = std::ios_base::cur;
00384   else if (whence == SEEK_END)
00385     wh = std::ios_base::end;
00386   else
00387     return -1;
00388 
00389   is->seekg (offset, wh);
00390 
00391   if (is->bad () || is->fail ())
00392     return -1;
00393 
00394   return 0;
00395 }
00396 
00397 /*
00398  * Deletes the istream, for ov_callbacks.
00399  */
00400 int
00401 close_istream (void *datasource)
00402 {
00403   delete (std::istream *) datasource;
00404   return 0;
00405 }
00406 
00407 /*
00408  * Wrapper around istream::tellg (), for ov_callbacks.
00409  */
00410 long
00411 tell_istream (void *datasource)
00412 {
00413   std::istream *is;
00414   is = (std::istream *) datasource;
00415   return is->tellg ();
00416 }
00417 
00418 
00419 /*
00420  * I/O functions for the Ogg/Vorbis decoder that operate on an istream.
00421  */
00422 const ov_callbacks istream_ogg_callbacks =
00423 {
00424   read_istream,
00425   seek_istream,
00426   close_istream,
00427   tell_istream
00428 };
00429 
00430 
00431 /*
00432  * A sound stream that reads Ogg/Vorbis from a file.
00433  */
00434 class ogg_sound_stream : public sound_stream
00435 {
00436 protected:
00437   bool finished;
00438   OggVorbis_File vorbis_file;
00439   size_t pcm_size;
00440   int channels;
00441   long frequency;
00442 
00443 protected:
00444   void
00445   initialise (std::istream *istr, const std::string &filename)
00446   {
00447     vorbis_info *info;
00448 
00449     this->finished = false;
00450 
00451     if (ov_open_callbacks (istr, &this->vorbis_file, NULL, 0,
00452                istream_ogg_callbacks) != 0)
00453       throw std::runtime_error ("Failed to open Ogg/Vorbis stream: "
00454                   + filename);
00455 
00456     this->pcm_size = ov_pcm_total (&this->vorbis_file, -1);
00457     if (this->pcm_size == (size_t) OV_EINVAL)
00458       this->pcm_size = 0;
00459 
00460     info = ov_info (&this->vorbis_file, -1);
00461     this->channels = info->channels;
00462     this->frequency = info->rate;
00463   }
00464 
00465 public:
00466   ogg_sound_stream (const std::string &filename)
00467   {
00468     std::istream *istr;
00469 
00470     istr = new std::ifstream (filename.c_str (),
00471                   std::ios_base::in | std::ios_base::binary);
00472     try
00473       {
00474     this->initialise (istr, filename);
00475       }
00476     catch (...)
00477       {
00478     delete istr;
00479     throw;
00480       }
00481   }
00482 
00483   ogg_sound_stream (std::istream *istr,
00484             const std::string &filename = "(input stream)")
00485   {
00486     this->initialise (istr, filename);
00487   }
00488 
00489   ~ogg_sound_stream (void)
00490   {
00491     ov_clear (&this->vorbis_file);
00492   }
00493 
00494   bool
00495   fill_buffer (sound_buffer *buf, size_t max_size)
00496   {
00497     char read_buffer[4096];
00498     std::vector<char> data;
00499     long x;
00500 #ifdef BOOST_LITTLE_ENDIAN
00501     const int endianness = 0;
00502 #else
00503     const int endianness = 1;
00504 #endif
00505 
00506     if (this->finished)
00507       return false;
00508 
00509     while (data.size () < max_size)
00510       {
00511     x = ov_read (&this->vorbis_file, read_buffer,
00512              std::min (sizeof (read_buffer), max_size - data.size ()),
00513              endianness, 2, 1, NULL);
00514     if (x <= 0)
00515       {
00516         if (x < 0)
00517           std::cerr << "Ogg/Vorbis read error " << x << std::endl;
00518 
00519         this->finished = true;
00520         break;
00521       }
00522 
00523     data.insert (data.end (), read_buffer, read_buffer + x);
00524       }
00525 
00526     if (data.empty ())
00527       return false;
00528 
00529     return buf->set_data (&data[0], data.size (), this->channels, 16,
00530               this->frequency);
00531   }
00532 
00533   size_t
00534   get_pcm_size (void)
00535   {
00536     return this->pcm_size;
00537   }
00538 
00539   bool
00540   is_finished (void)
00541   {
00542     return this->finished;
00543   }
00544 
00545   int
00546   get_channel_count (void)
00547   {
00548     return this->channels;
00549   }
00550 };
00551 
00552 
00553 /*
00554  * Microsoft WAV stream
00555  */
00556 
00557 template<typename T, int N>
00558 T
00559 read_little_endian (std::istream *s)
00560 {
00561   unsigned char byte;
00562   size_t x;
00563   T ret;
00564 
00565   ret = T (0);
00566 
00567   for (x = 0; x < N; x++)
00568     {
00569       //~ *s >> byte;   this operation sometimes reads 2 bytes instead of 1
00570       s->read((char *)&byte, 1);
00571       ret |= (byte << (x * 8));
00572     }
00573 
00574   return ret;
00575 }
00576 
00577 
00578 struct wav_format_header
00579 {
00580   const static std::string id;
00581 
00582   int format_tag;
00583   int channels;
00584   unsigned int samples_per_sec;
00585   unsigned int avg_bytes_per_sec;
00586   int block_align;
00587   int bits_per_sample;
00588 };
00589 
00590 const std::string wav_format_header::id = "fmt ";
00591 
00592 std::istream &
00593 operator>> (std::istream &str, wav_format_header &hdr)
00594 {
00595   hdr.format_tag        = read_little_endian<short, 2> (&str);
00596   hdr.channels          = read_little_endian<unsigned short, 2> (&str);
00597   hdr.samples_per_sec   = read_little_endian<unsigned int, 4> (&str);
00598   hdr.avg_bytes_per_sec = read_little_endian<unsigned int, 4> (&str);
00599   hdr.block_align       = read_little_endian<unsigned short, 2> (&str);
00600   hdr.bits_per_sample   = read_little_endian<unsigned short, 2> (&str);
00601   return str;
00602 }
00603 
00604 struct wav_data_header
00605 {
00606   const static std::string id;
00607 };
00608 
00609 const std::string wav_data_header::id = "data";
00610 
00611 class wav_sound_stream : public sound_stream
00612 {
00613 protected:
00614   std::istream *istr;
00615   unsigned int frequency;
00616   int channels;
00617   int bps;
00618   size_t pcm_size;
00619   size_t bytes_left;
00620   std::string filename;
00621 
00622 protected:
00623   void
00624   initialise (std::istream *istr, const std::string &filename)
00625   {
00626     wav_format_header fmt;
00627     std::ostringstream str;
00628     char chunk_id[5];
00629     size_t chunk_size;
00630     bool fmt_read = false;
00631     bool data_read = false;
00632 
00633     chunk_id[4] = 0;
00634 
00635     this->istr = istr;
00636     this->filename = filename;
00637 
00638     istr->read (chunk_id, 4);
00639     chunk_size = read_little_endian<int, 4> (istr);
00640     
00641     if (!istr->good ())
00642       throw std::runtime_error (filename + ": Read error");
00643 
00644     if (chunk_id != std::string ("RIFF"))
00645       throw std::runtime_error (filename + ": Not a MS WAV file");
00646 
00647     istr->read (chunk_id, 4);
00648     if (!istr->good ())
00649       throw std::runtime_error (filename + ": Not a MS WAV file");
00650 
00651     if (chunk_id != std::string ("WAVE"))
00652       throw std::runtime_error (filename + ": Not a MS WAV file");
00653 
00654     for (;;)
00655       {
00656     istr->read (chunk_id, 4);
00657     chunk_size = read_little_endian<int, 4> (istr);
00658 
00659     if (!istr->good ())
00660       throw std::runtime_error (filename + ": Read error");
00661 
00662     if (chunk_id == wav_format_header::id)
00663       {
00664         *istr >> fmt;
00665         if (!istr->good ())
00666           throw std::runtime_error (
00667               filename + ": Failed to read MS WAV format header");
00668 
00669         if (fmt.format_tag != 1)
00670           throw std::runtime_error (
00671               filename + ": Unsupported compressed MS WAV file");
00672 
00673         if (fmt.bits_per_sample != 16)
00674           {
00675         str.str ("");
00676         str << filename << ": Unsupported bits per sample "
00677             << fmt.bits_per_sample;
00678         throw std::runtime_error (str.str ());
00679           }
00680 
00681         this->frequency = fmt.samples_per_sec;
00682         this->channels = fmt.channels;
00683         this->bps = fmt.bits_per_sample;
00684 
00685         chunk_size -= 16;
00686         fmt_read = true;
00687       }
00688     else if (chunk_id == wav_data_header::id)
00689       {
00690         if (!fmt_read)
00691           throw std::runtime_error (
00692               filename + ": Data chunk before format chunk");
00693 
00694         this->bytes_left = chunk_size;
00695         this->pcm_size = chunk_size / (this->channels * this->bps / 8);
00696 
00697         data_read = true;
00698         break;
00699       }
00700 
00701     if (chunk_size > 0)
00702       {
00703         istr->seekg (chunk_size, std::ios_base::cur);
00704         if (!istr->good ())
00705           throw std::runtime_error (filename + ": Seek error");
00706       }
00707       }
00708   }
00709 
00710 public:
00711   wav_sound_stream (std::istream *istr,
00712             const std::string &filename = "(input stream)")
00713   {
00714     this->initialise (istr, filename);
00715   }
00716 
00717   wav_sound_stream (const std::string &filename)
00718   {
00719     std::ifstream *istr;
00720 
00721     istr = new std::ifstream (filename.c_str (),
00722                   std::ios_base::binary | std::ios_base::in);
00723     try
00724       {
00725     this->initialise (istr, filename);
00726       }
00727     catch (...)
00728       {
00729     delete istr;
00730     throw;
00731       }
00732   }
00733 
00734   ~wav_sound_stream (void)
00735   {
00736     delete this->istr;
00737   }
00738 
00739   size_t
00740   get_pcm_size (void)
00741   {
00742     return this->pcm_size;
00743   }
00744 
00745   bool
00746   fill_buffer (sound_buffer *buf, size_t max_size)
00747   {
00748     char read_buffer[2048];
00749     std::vector<char> data;
00750     std::streamsize read;
00751 
00752     if (this->bytes_left == 0)
00753       return false;
00754 
00755     max_size = std::min (max_size, this->bytes_left);
00756 
00757     while (data.size () < max_size)
00758       {
00759     this->istr->read (read_buffer, std::min (data.size () - max_size,
00760                          sizeof (read_buffer)));
00761     read = this->istr->gcount ();
00762     if (read == 0)
00763       {
00764         if (this->bytes_left > 0)
00765           std::cerr << this->filename << ": Unexpected EOF" << std::endl;
00766 
00767         this->bytes_left = 0;
00768         break;
00769       }
00770 
00771     this->bytes_left -= read;
00772 
00773 #ifndef BOOST_LITTLE_ENDIAN
00774     if (this->bps == 16)
00775       {
00776         std::streamsize s;
00777         char tmp;
00778 
00779         for (s = 1; s < read; s += 2)
00780           {
00781         tmp = read_buffer[s - 1];
00782         read_buffer[s - 1] = read_buffer[s];
00783         read_buffer[s] = tmp;
00784           }
00785       }
00786 #endif
00787 
00788     data.insert (data.end (), read_buffer, read_buffer + read);
00789       }
00790 
00791     if (data.empty ())
00792       return false;
00793 
00794     return buf->set_data (&data[0], data.size (), this->channels,
00795               this->bps, this->frequency);
00796   }
00797 
00798   bool
00799   is_finished (void)
00800   {
00801     return (this->bytes_left == 0);
00802   }
00803 
00804   int
00805   get_channel_count (void)
00806   {
00807     return this->channels;
00808   }
00809 };
00810 
00811 
00812 void
00813 transform_point (float x, float y, float z,
00814          float distance_factor,
00815          float *rx, float *ry, float *rz)
00816 {
00817   *rx = x * distance_factor;
00818   *ry = y * distance_factor;
00819   *rz = z * distance_factor;
00820 }
00821 
00822 void
00823 inverse_transform_point (float x, float y, float z,
00824              float distance_factor,
00825              float *rx, float *ry, float *rz)
00826 {
00827   *rx = x / distance_factor;
00828   *ry = y / distance_factor;
00829   *rz = z / distance_factor;
00830 }
00831 
00832 inline bool
00833 operator< (const boost::xtime &t0, const boost::xtime &t1)
00834 {
00835   return (boost::xtime_cmp (t0, t1) < 0);
00836 }
00837 
00838 inline void
00839 xtime_diff (const boost::xtime *t0, const boost::xtime *t1,
00840         boost::xtime *ret)
00841 {
00842   if (*t0 < *t1)
00843     {
00844       ret->sec = 0;
00845       ret->nsec = 0;
00846     }
00847 
00848   ret->sec = t0->sec - t1->sec;
00849   ret->nsec = t0->nsec - t1->nsec;
00850   
00851   if (ret->nsec < 0)
00852     {
00853       ret->sec -= 1;
00854       ret->nsec += 1000*1000*1000;
00855     }
00856 }
00857 
00858 
00859 struct xtime_less 
00860 {
00861   inline bool
00862   operator() (const boost::xtime *t0, const boost::xtime *t1) const
00863   {
00864     return (*t0 < *t1);
00865   }
00866 };
00867 
00868 
00869 OpenALSoundSystem *sound_system = NULL;
00870 
00871 } /* anonymous namespace */
00872 
00873 
00874 /*
00875  *
00876  * Class declarations
00877  *
00878  */
00879 
00880 /*
00881  * Base OpenAL sound source.
00882  */
00883 class OpenALSoundSource : public cSoundSource
00884 {
00885 private:
00886   bool playing; /* playing or paused */
00887   bool three_d;
00888 
00889 protected:
00890   ALuint name;
00891   OpenALSoundSystem * const system;
00892   DECLARE_MUTEX;
00893 
00894 protected:
00895   OpenALSoundSource (OpenALSoundSystem *system, bool three_d);
00896   ~OpenALSoundSource (void);
00897 
00898 public:
00899   const bool Play (void);
00900   const bool IsPlaying (void);
00901   const bool IsPaused (void);
00902   void Stop (void);
00903   void Pause (void);
00904   void SetVolume (const float volume);
00905   const float GetVolume (void);
00906   void SetMinMaxDistance (const float min, const float max);
00907   void GetMinMaxDistance (float &min, float &max);
00908   bool Is3D (void);
00909   void SetPosition (const float x, const float y, const float z);
00910   void SetVelocity (const float x, const float y, const float z);
00911   void GetPosition (float &x, float &y, float &z);
00912   void GetVelocity (float &x, float &y, float &z);
00913 
00914   virtual void update (void) = 0;
00915 };
00916 
00917 
00918 /*
00919  * A source whose data is completely loaded into memory.  Used for playing
00920  * short and frequently used sounds.
00921  */
00922 class SingleBufferSource : public OpenALSoundSource
00923 {
00924 protected:
00925   sound_buffer *buffer;
00926 
00927 public:
00928   SingleBufferSource (OpenALSoundSystem *system, sound_buffer *buffer,
00929               bool three_d = true);
00930   ~SingleBufferSource (void);
00931   void update (void);
00932 };
00933 
00934 
00935 /*
00936  * A source whose data is loaded into memory as it plays.  Used for playing
00937  * music or long sounds.
00938  */
00939 class StreamSource : public OpenALSoundSource
00940 {
00941 protected:
00942   std::map<ALuint, sound_buffer *> buffers;
00943   sound_stream *stream;
00944 
00945 public:
00946   StreamSource (OpenALSoundSystem *system, sound_stream *stream,
00947         bool three_d = true);
00948   ~StreamSource (void);
00949   void update (void);
00950 };
00951 
00952 /*
00953  * This class is used for whenver a source cannot be created.  When this
00954  * happens, an instance of NullSource is returned instead of returning NULL or
00955  * throwing an exception.
00956  */
00957 class NullSource : public OpenALSoundSource
00958 {
00959 public:
00960   NullSource (OpenALSoundSystem *sys) : OpenALSoundSource (sys, false) { };
00961   void update (void) { }
00962 };
00963 
00964 class OpenALSoundSystem : public cSoundSystem
00965 {
00966 protected:
00967   typedef std::map<std::string, shared_buffer *> shared_buffer_map;
00968   typedef std::set<OpenALSoundSource *> source_set;
00969   //typedef std::multimap<boost::xtime *, shared_buffer *, xtime_less>
00970   typedef std::multimap<long, shared_buffer *>
00971     shared_buffer_time_map;
00972 
00973 private:
00974   void register_shared_buffer (shared_buffer *buf);
00975   shared_buffer *get_shared_buffer (const std::string &id);
00976 
00977 protected:
00978   ALCcontext *context;
00979   ALCdevice *device;
00980   float distance_factor;
00981   shared_buffer_map shared_buffers;
00982   shared_buffer_time_map unused_buffers;
00983   source_set sources;
00984   bool buffers_queued;
00985 #ifdef ENABLE_THREADS
00986   DECLARE_MUTEX;
00987   boost::thread *thread;
00988   bool finished;
00989 #endif
00990 
00991 public:
00992   OpenALSoundSystem (int frequency);
00993   ~OpenALSoundSystem (void);
00994   void SetListenerPosition (const float x, const float y, const float z);   
00995   void SetListenerVelocity (const float x, const float y, const float z);
00996   void GetListenerPosition (float &x, float &y, float &z);
00997   void GetListenerVelocity (float &x, float &y, float &z);
00998   void SetVolume (const float volume);
00999   const float GetVolume (void);
01000   void SetDistanceFactor (const float s);
01001   const float GetDistanceFactor (void);
01002   cSoundSource *CreateSoundSource (const char *filename);
01003   cSoundSource *CreateSoundSource (const char *buffer, const int size,
01004                    const int channels, const int bps,
01005                    const int frequency);
01006   cSoundSource *CreateSoundSource3D (const float x, const float y,
01007                      const float z, const char *filename);
01008   cSoundSource *CreateSoundSource3D (const float x, const float y,
01009                      const float z, const char *buffer,
01010                      const int size, const int channels,
01011                      const int bps, const int frequency);
01012   void Step (void);
01013 
01014   void release_buffer (sound_buffer *buf);
01015   bool update (void);
01016   void notify_playing (OpenALSoundSource *source);
01017   void notify_stopped (OpenALSoundSource *source);
01018 };
01019 
01020 
01021 #ifdef ENABLE_THREADS
01022 class OpenALSoundThread
01023 {
01024 protected:
01025   OpenALSoundSystem *system;
01026 
01027 public:
01028   OpenALSoundThread (OpenALSoundSystem *system);
01029   void operator() (void);
01030 };
01031 #endif
01032 
01033 
01034 /*
01035  *
01036  * Implementation of major classes.
01037  *
01038  */
01039 
01040 
01041 /*
01042  *
01043  * OpenALSoundSource
01044  *
01045  */
01046 
01047 OpenALSoundSource::OpenALSoundSource (OpenALSoundSystem *sys, bool three_d)
01048   : system (sys)
01049 {
01050   alGenSources (1, &this->name);
01051   CHECK_OPENAL;
01052 
01053   this->three_d = three_d;
01054   this->playing = false;
01055 
01056   if (!this->three_d)
01057     {
01058       alSourcei (this->name, AL_SOURCE_RELATIVE, AL_TRUE);
01059       CHECK_OPENAL;
01060       alSourcef (this->name, AL_ROLLOFF_FACTOR, 0.f);
01061       CHECK_OPENAL;
01062     }
01063 }
01064 
01065 OpenALSoundSource::~OpenALSoundSource (void)
01066 {
01067   this->Stop ();
01068   alDeleteSources (1, &this->name);
01069 }
01070 
01071 const bool
01072 OpenALSoundSource::Play (void)
01073 {
01074   ALenum state;
01075   ALint count;
01076 
01077   HOLD_LOCK;
01078 
01079   /*
01080    * Stupidly enough, OpenAL gives an error if alSourcePlay () is called on a
01081    * source does not have queued buffers, so we need to check for that.
01082    */
01083   alGetSourcei (this->name, AL_BUFFERS_QUEUED, &count);
01084   CHECK_OPENAL;
01085 
01086   if (count == 0)
01087     return this->playing;
01088 
01089   alGetSourcei (this->name, AL_SOURCE_STATE, &state);
01090   CHECK_OPENAL;
01091 
01092   if (state == AL_PLAYING)
01093     {
01094       assert (this->playing);
01095       return true;
01096     }
01097 
01098   alSourcePlay (this->name);
01099   CHECK_OPENAL;
01100 
01101   alGetSourcei (this->name, AL_SOURCE_STATE, &state);
01102   CHECK_OPENAL;
01103 
01104   this->playing = (state == AL_PLAYING);
01105   if (this->playing)
01106     this->system->notify_playing (this);
01107 
01108   return this->playing;
01109 }
01110 
01111 const bool
01112 OpenALSoundSource::IsPlaying (void)
01113 {
01114   HOLD_LOCK;
01115   return this->playing;
01116 }
01117 
01118 const bool
01119 OpenALSoundSource::IsPaused (void)
01120 {
01121   ALint state;
01122 
01123   HOLD_LOCK;
01124 
01125   if (!this->playing)
01126     return false;
01127 
01128   alGetSourcei (this->name, AL_SOURCE_STATE, &state);
01129   CHECK_OPENAL;
01130 
01131   return (state == AL_PAUSED);
01132 }
01133 
01134 void
01135 OpenALSoundSource::Stop (void)
01136 {
01137   HOLD_LOCK;
01138 
01139   if (!this->playing)
01140     return;
01141 
01142   alSourceStop (this->name);
01143   CHECK_OPENAL;
01144 
01145   this->playing = false;
01146   this->system->notify_stopped (this);
01147 }
01148 
01149 void
01150 OpenALSoundSource::Pause (void)
01151 {
01152   HOLD_LOCK;
01153 
01154   if (!this->playing)
01155     return;
01156 
01157   alSourcePause (this->name);
01158   CHECK_OPENAL;
01159 }
01160 
01161 void
01162 OpenALSoundSource::SetVolume (const float volume)
01163 {
01164   float clamped_volume;
01165 
01166   HOLD_LOCK;
01167 
01168   clamped_volume = std::max (0.f, std::min (volume, 1.f));
01169   alSourcef (this->name, AL_GAIN, clamped_volume);
01170   CHECK_OPENAL;
01171 }
01172 
01173 const float
01174 OpenALSoundSource::GetVolume (void)
01175 {
01176   float volume;
01177 
01178   HOLD_LOCK;
01179 
01180   alGetSourcef (this->name, AL_GAIN, &volume);
01181   CHECK_OPENAL;
01182 
01183   return volume;
01184 }
01185 
01186 void
01187 OpenALSoundSource::SetMinMaxDistance (const float min, const float max)
01188 {
01189   float tmax;
01190 
01191   HOLD_LOCK;
01192 
01193   tmax = max * this->system->GetDistanceFactor ();
01194   alSourcef (this->name, AL_MAX_DISTANCE, max);
01195   CHECK_OPENAL;
01196 }
01197 
01198 void
01199 OpenALSoundSource::GetMinMaxDistance (float &min, float &max)
01200 {
01201   float tmax;
01202 
01203   HOLD_LOCK;
01204 
01205   alGetSourcef (this->name, AL_MAX_DISTANCE, &tmax);
01206   CHECK_OPENAL;
01207 
01208   min = 0.f;
01209   max = tmax / this->system->GetDistanceFactor ();
01210 }
01211 
01212 bool
01213 OpenALSoundSource::Is3D (void)
01214 {
01215   HOLD_LOCK;
01216 
01217   return this->three_d;
01218 }
01219 
01220 void
01221 OpenALSoundSource::SetPosition (const float x, const float y, const float z)
01222 {
01223   float tx, ty, tz;
01224 
01225   HOLD_LOCK;
01226 
01227   if (!this->three_d)
01228     return;
01229 
01230   transform_point (x, y, z,
01231            this->system->GetDistanceFactor (),
01232            &tx, &ty, &tz);
01233   alSource3f (this->name, AL_POSITION, tx, ty, tz);
01234   CHECK_OPENAL;
01235 }
01236 
01237 void
01238 OpenALSoundSource::SetVelocity (const float x, const float y, const float z)
01239 {
01240   float tx, ty, tz;
01241 
01242   HOLD_LOCK;
01243 
01244   if (!this->three_d)
01245     return;
01246 
01247   transform_point (x, y, z, 
01248            this->system->GetDistanceFactor (),
01249            &tx, &ty, &tz);
01250   alSource3f (this->name, AL_VELOCITY, tx, ty, tz);
01251   CHECK_OPENAL;
01252 }
01253 
01254 void
01255 OpenALSoundSource::GetPosition (float &x, float &y, float &z)
01256 {
01257   float tx, ty, tz;
01258 
01259   HOLD_LOCK;
01260 
01261   if (!this->three_d)
01262     {
01263       x = 0.f;
01264       y = 0.f;
01265       z = 0.f;
01266       return;
01267     }
01268 
01269   alGetSource3f (this->name, AL_POSITION, &tx, &ty, &tz);
01270   CHECK_OPENAL;
01271 
01272   inverse_transform_point (tx, ty, tz,
01273                this->system->GetDistanceFactor (),
01274                &x, &y, &z);
01275 }
01276 
01277 void
01278 OpenALSoundSource::GetVelocity (float &x, float &y, float &z)
01279 {
01280   float tx, ty, tz;
01281 
01282   HOLD_LOCK;
01283 
01284   if (!this->three_d)
01285     {
01286       x = 0.f;
01287       y = 0.f;
01288       z = 0.f;
01289       return;
01290     }
01291 
01292   alGetSource3f (this->name, AL_VELOCITY, &tx, &ty, &tz);
01293   CHECK_OPENAL;
01294 
01295   inverse_transform_point (tx, ty, tz,
01296                this->system->GetDistanceFactor (),
01297                &x, &y, &z);
01298 }
01299 
01300 
01301 /*
01302  *
01303  * OpenALSoundSystem
01304  *
01305  */
01306 
01307 OpenALSoundSystem::OpenALSoundSystem (int frequency)
01308 {
01309   ALint ctx_attrs[] = {ALC_FREQUENCY, frequency, 0};
01310 
01311   this->device = NULL;
01312   this->context = NULL;
01313   this->SetDistanceFactor (1.f);
01314 
01315   try
01316     {
01317       this->device = alcOpenDevice (NULL);
01318       if (this->device == NULL)
01319     throw std::runtime_error ("OpenAL alcOpenDevice failed");
01320 
01321       this->context = alcCreateContext (this->device, ctx_attrs);
01322       if (this->context == NULL)
01323     {
01324       std::cerr << "Warning: Failed to allocate OpenAL context."
01325             << "  Retrying with default attributes." << std::endl;
01326       this->context = alcCreateContext (this->device, NULL);
01327       if (this->context == NULL)
01328           throw std::runtime_error ("OpenAL alcCreateContext failed");
01329     }
01330 
01331       if (!alcMakeContextCurrent (this->context))
01332         throw std::runtime_error ("OpenAL failed to activate context");
01333     }
01334   catch (...)
01335     {
01336       if (this->context != NULL)
01337     alcDestroyContext (this->context);
01338 
01339       if (this->device != NULL)
01340     alcCloseDevice (this->device);
01341 
01342       throw;
01343     }
01344 
01345 #ifdef ENABLE_THREADS
01346   this->finished = false;
01347   this->thread = new boost::thread (OpenALSoundThread (this));
01348 #endif
01349 }
01350 
01351 OpenALSoundSystem::~OpenALSoundSystem (void)
01352 {
01353 #ifdef ENABLE_THREADS
01354   {
01355     HOLD_LOCK;
01356     this->finished = true;
01357   }
01358 
01359   this->thread->join ();
01360   delete this->thread;
01361 #endif
01362 
01363   alcMakeContextCurrent (NULL);
01364   alcDestroyContext (this->context);
01365   alcCloseDevice (this->device);
01366 }
01367 
01368 void
01369 OpenALSoundSystem::SetListenerPosition (const float x, const float y,
01370                         const float z)
01371 {
01372   float tx, ty, tz;
01373 
01374   HOLD_LOCK;
01375 
01376   transform_point (x, y, z, this->distance_factor, &tx, &ty, &tz);
01377   alListener3f (AL_POSITION, tx, ty, tz);
01378   CHECK_OPENAL;
01379 }
01380 
01381 void
01382 OpenALSoundSystem::SetListenerVelocity (const float x, const float y,
01383                         const float z)
01384 {
01385   float tx, ty, tz;
01386 
01387   HOLD_LOCK;
01388 
01389   transform_point (x, y, z, this->distance_factor, &tx, &ty, &tz);
01390   alListener3f (AL_VELOCITY, tx, ty, tz);
01391   CHECK_OPENAL;
01392 }
01393 
01394 void
01395 OpenALSoundSystem::GetListenerPosition (float &x, float &y, float &z)
01396 {
01397   float tx, ty, tz;
01398 
01399   HOLD_LOCK;
01400 
01401   alGetListener3f (AL_POSITION, &tx, &ty, &tz);
01402   CHECK_OPENAL;
01403 
01404   inverse_transform_point (tx, ty, tz, this->distance_factor, &x, &y, &z);
01405 }
01406 
01407 void
01408 OpenALSoundSystem::GetListenerVelocity (float &x, float &y, float &z)
01409 {
01410   float tx, ty, tz;
01411 
01412   HOLD_LOCK;
01413 
01414   alGetListener3f (AL_VELOCITY, &tx, &ty, &tz);
01415   CHECK_OPENAL;
01416   inverse_transform_point (tx, ty, tz, this->distance_factor, &x, &y, &z);
01417 }
01418 
01419 void
01420 OpenALSoundSystem::SetVolume (const float volume)
01421 {
01422   float clamped_volume;
01423 
01424   HOLD_LOCK;
01425 
01426   clamped_volume = std::max (0.f, std::min (volume, 1.f));
01427   alListenerf (AL_GAIN, clamped_volume);
01428   CHECK_OPENAL;
01429 }
01430 
01431 const float
01432 OpenALSoundSystem::GetVolume (void)
01433 {
01434   float volume;
01435 
01436   HOLD_LOCK;
01437 
01438   alGetListenerf (AL_GAIN, &volume);
01439   CHECK_OPENAL;
01440 
01441   return volume;
01442 }
01443 
01444 void
01445 OpenALSoundSystem::SetDistanceFactor (const float s)
01446 {
01447   HOLD_LOCK;
01448 
01449   this->distance_factor = s;
01450 }
01451 
01452 const float
01453 OpenALSoundSystem::GetDistanceFactor (void)
01454 {
01455   HOLD_LOCK;
01456 
01457   return this->distance_factor;
01458 }
01459 
01460 cSoundSource *
01461 OpenALSoundSystem::CreateSoundSource (const char *filename)
01462 {
01463   return this->CreateSoundSource3D (0.f, 0.f, 0.f, filename);
01464 }
01465 
01466 cSoundSource *
01467 OpenALSoundSystem::CreateSoundSource (const char *buffer, const int size,
01468                       const int channels, const int bps,
01469                       const int frequency)
01470 {
01471   return this->CreateSoundSource3D (0.f, 0.f, 0.f, buffer, size, channels,
01472                     bps, frequency);
01473 }
01474 
01475 cSoundSource *
01476 OpenALSoundSystem::CreateSoundSource3D (const float x, const float y,
01477                     const float z, const char *filename)
01478 {
01479   std::string fname (filename);
01480   std::string extension;
01481   sound_stream *str;
01482   shared_buffer *sbuf;
01483   size_t size;
01484   std::string::size_type n;
01485   OpenALSoundSource *source;
01486 
01487   HOLD_LOCK;
01488 
01489   /*
01490    * If this sound is already loaded into a shared buffer, use it.
01491    */
01492   sbuf = this->get_shared_buffer (fname);
01493   if (sbuf != NULL)
01494     {
01495       source = new SingleBufferSource (this, sbuf);
01496       source->SetPosition (x, y, z);
01497       return source;
01498     }
01499 
01500   n = fname.find_last_of (".");
01501   if (n == std::string::npos)
01502     {
01503       std::cerr << "OpenAL: Unknown file type: " << fname << std::endl;
01504       return new NullSource (this);
01505     }
01506 
01507   extension = fname.substr (n + 1);
01508 
01509   try
01510     {
01511       if (extension == "ogg")
01512     str = new ogg_sound_stream (fname);
01513       else if (extension == "wav")
01514     str = new wav_sound_stream (fname);
01515       else
01516     throw std::runtime_error ("OpenAL: Unsupported file type: " + fname);
01517     }
01518   catch (const std::runtime_error &err)
01519     {
01520       std::cerr << err.what () << std::endl;
01521       return new NullSource (this);
01522     }
01523 
01524   size = 2 * str->get_pcm_size () * str->get_channel_count ();
01525 
01526   /*
01527    * Check if the sound is small enough to fit into one buffer.
01528    */
01529   if (size != 0 && size < MAXIMUM_BUFFER_SIZE)
01530     {
01531       sbuf = new shared_buffer (fname);
01532 
01533       if (!str->fill_buffer (sbuf, SIZE_MAX))
01534     {
01535       std::cerr << "OpenAL could not fill buffer from " << fname
01536                 << std::endl;
01537       delete str;
01538       return new NullSource (this);
01539     }
01540 
01541       delete str;
01542 
01543       this->register_shared_buffer (sbuf);
01544       source = new SingleBufferSource (this, sbuf);
01545       source->SetPosition (x, y, z);
01546       return source;
01547     }
01548 
01549   /*
01550    * The sound is too big to be kept in memory, so use streaming.
01551    */
01552   source = new StreamSource (this, str);
01553   source->SetPosition (x, y, z);
01554   return source;
01555 }
01556 
01557 cSoundSource *
01558 OpenALSoundSystem::CreateSoundSource3D (const float x, const float y,
01559                     const float z, const char *buffer,
01560                     const int size, const int channels,
01561                     const int bps, const int frequency)
01562 {
01563   sound_buffer *sbuf;
01564 
01565   HOLD_LOCK;
01566 
01567   sbuf = new sound_buffer;
01568 
01569   if (!sbuf->set_data (buffer, size, channels, bps, frequency))
01570     {
01571       delete sbuf;
01572       return new NullSource (this);
01573     }
01574 
01575   return new SingleBufferSource (this, sbuf, true);
01576 }
01577 
01578 void
01579 OpenALSoundSystem::Step (void)
01580 {
01581 #ifndef ENABLE_THREADS
01582   this->update ();
01583 #endif
01584 }
01585 
01586 void
01587 OpenALSoundSystem::release_buffer (sound_buffer *buf)
01588 {
01589   HOLD_LOCK;
01590 
01591   switch (buf->get_type ())
01592   {
01593     case sound_buffer::PLAIN:
01594       delete buf;
01595       break;
01596 
01597     case sound_buffer::SHARED:
01598       shared_buffer *sbuf;
01599 
01600       sbuf = (shared_buffer *) buf;
01601       sbuf->dec_ref ();
01602 
01603       if (sbuf->get_ref_count () == 0)
01604     this->unused_buffers.insert (
01605       std::make_pair (sbuf->last_use_time, sbuf));
01606 
01607       break;
01608   }
01609 }
01610 
01611 void
01612 OpenALSoundSystem::notify_playing (OpenALSoundSource *source)
01613 {
01614   this->sources.insert (source);
01615 }
01616 
01617 void
01618 OpenALSoundSystem::notify_stopped (OpenALSoundSource *source)
01619 {
01620   this->sources.erase (source);
01621 }
01622 
01623 void
01624 OpenALSoundSystem::register_shared_buffer (shared_buffer *sbuf)
01625 {
01626   this->shared_buffers[sbuf->id] = sbuf;
01627 }
01628 
01629 shared_buffer *
01630 OpenALSoundSystem::get_shared_buffer (const std::string &id)
01631 {
01632   shared_buffer_map::iterator itr;
01633   shared_buffer *sbuf;
01634   shared_buffer_time_map::iterator sbitr;
01635 
01636   itr = this->shared_buffers.find (id);
01637   if (itr == this->shared_buffers.end ())
01638     return NULL;
01639 
01640   sbuf = itr->second;
01641 
01642   if (sbuf->get_ref_count () == 0)
01643     {
01644       sbitr = this->unused_buffers.begin ();
01645 
01646       do
01647     {
01648           if (sbitr->second->id == id)
01649         this->unused_buffers.erase (sbitr++);
01650       else
01651         ++sbitr;
01652     }
01653       while (sbitr != this->unused_buffers.end ());
01654     }
01655 
01656   sbuf->inc_ref ();
01657 
01658   return sbuf;
01659 }
01660 
01661 bool
01662 OpenALSoundSystem::update (void)
01663 {
01664   source_set::iterator itr;
01665   shared_buffer_time_map::iterator sbitr;
01666   shared_buffer *sbuf;
01667   OpenALSoundSource *source;
01668   //boost::xtime xt, diff;
01669 
01670   HOLD_LOCK;
01671 
01672 #ifdef ENABLE_THREADS
01673   if (this->finished)
01674     return false;
01675 #endif
01676 
01677   if (!this->sources.empty ())
01678     {
01679       itr = this->sources.begin ();
01680 
01681       do
01682     {
01683       source = *itr++;
01684       source->update ();
01685     }
01686       while (itr != this->sources.end ());
01687     }
01688 
01689   //boost::xtime_get (&xt, boost::TIME_UTC);
01690 
01691   while (this->unused_buffers.size () > IMMORTAL_SHARED_BUFFER_COUNT)
01692     {
01693       sbitr = this->unused_buffers.begin ();
01694       //xtime_diff (&xt, sbitr->first, &diff);
01695       long diff = cShell::GetTicks() - sbitr->first;
01696       if (diff < SHARED_BUFFER_TIMEOUT * 1000)
01697       //if (diff.sec < SHARED_BUFFER_TIMEOUT)
01698     break;
01699 
01700       sbuf = sbitr->second;
01701 
01702       this->shared_buffers.erase (sbuf->id);
01703       this->unused_buffers.erase (sbitr);
01704       delete sbuf;
01705     }
01706 
01707   return true;
01708 }
01709 
01710 
01711 /*
01712  *
01713  * SingleBufferSource
01714  *
01715  */
01716 
01717 SingleBufferSource::SingleBufferSource (OpenALSoundSystem *system,
01718                     sound_buffer *buffer,
01719                     bool three_d)
01720  : OpenALSoundSource (system, three_d && buffer->get_channel_count () == 1)
01721 {
01722   this->buffer = buffer;
01723   alSourceQueueBuffers (this->name, 1, &buffer->name);
01724   CHECK_OPENAL;
01725 }
01726 
01727 SingleBufferSource::~SingleBufferSource (void)
01728 {
01729   this->system->release_buffer (this->buffer);
01730 }
01731 
01732 void
01733 SingleBufferSource::update (void)
01734 {
01735   ALint count;
01736   ALuint buf;
01737 
01738   HOLD_LOCK;
01739 
01740   if (!this->IsPlaying ())
01741     return;
01742 
01743   alGetSourcei (this->name, AL_BUFFERS_PROCESSED, &count);
01744   CHECK_OPENAL;
01745 
01746   if (count == 0)
01747     return;
01748 
01749   this->Stop ();
01750   alSourceUnqueueBuffers (this->name, 1, &buf);
01751   CHECK_OPENAL;
01752 }
01753 
01754 
01755 /*
01756  *
01757  * StreamSource
01758  *
01759  */
01760 
01761 StreamSource::StreamSource (OpenALSoundSystem *system, sound_stream *stream,
01762                 bool three_d)
01763   : OpenALSoundSource (system, three_d && stream->get_channel_count () == 1)
01764 {
01765   int x;
01766   sound_buffer *buffer;
01767 
01768   this->stream = stream;
01769 
01770   for (x = 0; x < STREAM_BUFFER_COUNT; x++)
01771     {
01772       buffer = new sound_buffer;
01773       
01774       if (!stream->fill_buffer (buffer))
01775     {
01776       this->system->release_buffer (buffer);
01777       break;
01778     }
01779 
01780       this->buffers[buffer->name] = buffer;
01781       alSourceQueueBuffers (this->name, 1, &buffer->name);
01782       CHECK_OPENAL;
01783     }
01784 }
01785 
01786 StreamSource::~StreamSource (void)
01787 {
01788   std::map<ALuint, sound_buffer *>::iterator itr;
01789 
01790   for (itr = this->buffers.begin (); itr != this->buffers.end (); ++itr)
01791     this->system->release_buffer (itr->second);
01792 
01793   delete this->stream;
01794 }
01795 
01796 void
01797 StreamSource::update (void)
01798 {
01799   ALint count;
01800   ALuint name;
01801   ALenum state;
01802   sound_buffer *buffer;
01803 
01804   HOLD_LOCK;
01805 
01806   if (!this->IsPlaying ())
01807     return;
01808 
01809   alGetSourcei (this->name, AL_BUFFERS_PROCESSED, &count);
01810   CHECK_OPENAL;
01811 
01812   while (count-- > 0)
01813     {
01814       alSourceUnqueueBuffers (this->name, 1, &name);
01815       CHECK_OPENAL;
01816       buffer = this->buffers[name];
01817 
01818       if (!this->stream->fill_buffer (buffer))
01819     break;
01820 
01821       alSourceQueueBuffers (this->name, 1, &name);
01822       CHECK_OPENAL;
01823     }
01824 
01825   alGetSourcei (this->name, AL_SOURCE_STATE, &state);
01826   CHECK_OPENAL;
01827 
01828   if (state == AL_STOPPED)
01829     {
01830       alGetSourcei (this->name, AL_BUFFERS_QUEUED, &count);
01831       CHECK_OPENAL;
01832 
01833       if (count > 0 || !this->stream->is_finished ())
01834     {
01835       alSourcePlay (this->name);
01836       CHECK_OPENAL;
01837     }
01838       else
01839     this->Stop ();
01840     }
01841 }
01842 
01843 /*
01844  *
01845  * OpenALSoundThread
01846  *
01847  */
01848 
01849 #ifdef ENABLE_THREADS
01850 OpenALSoundThread::OpenALSoundThread (OpenALSoundSystem *system)
01851 {
01852   this->system = system;
01853 }
01854 
01855 void
01856 OpenALSoundThread::operator() (void)
01857 {
01858   boost::xtime xt;
01859 
01860   while (system->update ())
01861     {
01862       boost::xtime_get (&xt, boost::TIME_UTC);
01863       xt.nsec += 20*1000*1000;
01864       if (xt.nsec > 1000*1000*1000)
01865     {
01866       xt.sec += 1;
01867       xt.nsec -= 1000*1000*1000;
01868     }
01869 
01870       boost::thread::sleep (xt);
01871     }
01872 }
01873 #endif
01874 
01875 
01876 /*
01877  *
01878  * CreateOpenALSoundSystem
01879  *
01880  */
01881 
01882 cSoundSystem *
01883 CreateOpenALSoundSystem (int frequency)
01884 {
01885   if (sound_system != NULL)
01886     return NULL;
01887 
01888   try
01889     {
01890       sound_system = new OpenALSoundSystem (frequency);
01891     }
01892   catch (const std::runtime_error &err)
01893     {
01894       std::cerr << err.what () << std::endl;
01895       return NULL;
01896     }
01897 
01898   return sound_system;
01899 }
01900 
01901 } /* namespace Lugre */
01902 
01903 #endif /* USE_OPENAL */

Generated on Wed May 23 06:00:15 2012 for cpp by  doxygen 1.5.6