00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
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
00068
00069 #define STREAM_BUFFER_SIZE (32 * 1024)
00070
00071
00072
00073
00074 #define STREAM_BUFFER_COUNT 4
00075
00076
00077
00078
00079
00080 #define MAXIMUM_BUFFER_SIZE (256 * 1024)
00081
00082
00083
00084
00085
00086 #define SHARED_BUFFER_TIMEOUT 120
00087
00088
00089
00090
00091 #define IMMORTAL_SHARED_BUFFER_COUNT 16
00092
00093
00094
00095
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
00119
00120 class OpenALSoundSystem;
00121
00122
00123
00124
00125
00126
00127
00128
00129 namespace
00130 {
00131
00132 inline void
00133 check_openal (int line)
00134 {
00135 ALenum err;
00136
00137
00138 err = alGetError ();
00139 if (err == AL_NO_ERROR)
00140 return;
00141
00142
00143
00144 }
00145
00146
00147
00148
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
00242
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
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
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
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
00309 else
00310 return this->last_use_time;
00311
00312 }
00313
00314
00315
00316
00317
00318 class sound_stream
00319 {
00320 public:
00321 virtual ~sound_stream (void) { }
00322
00323
00324
00325
00326 virtual size_t get_pcm_size (void) = 0;
00327
00328
00329
00330
00331
00332 virtual bool fill_buffer (sound_buffer *buf,
00333 size_t max_size = STREAM_BUFFER_SIZE) = 0;
00334
00335
00336
00337
00338 virtual bool is_finished (void) = 0;
00339
00340
00341
00342
00343
00344 virtual int get_channel_count (void) = 0;
00345 };
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355
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
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
00399
00400 int
00401 close_istream (void *datasource)
00402 {
00403 delete (std::istream *) datasource;
00404 return 0;
00405 }
00406
00407
00408
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
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
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
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
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 }
00872
00873
00874
00875
00876
00877
00878
00879
00880
00881
00882
00883 class OpenALSoundSource : public cSoundSource
00884 {
00885 private:
00886 bool playing;
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
00920
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
00937
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
00954
00955
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
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
01037
01038
01039
01040
01041
01042
01043
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
01081
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
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
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
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
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
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
01690
01691 while (this->unused_buffers.size () > IMMORTAL_SHARED_BUFFER_COUNT)
01692 {
01693 sbitr = this->unused_buffers.begin ();
01694
01695 long diff = cShell::GetTicks() - sbitr->first;
01696 if (diff < SHARED_BUFFER_TIMEOUT * 1000)
01697
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
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
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
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
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 }
01902
01903 #endif