src/audio/sound_manager.cpp

Go to the documentation of this file.
00001 //  SuperTux
00002 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
00003 //
00004 //  This program is free software: you can redistribute it and/or modify
00005 //  it under the terms of the GNU General Public License as published by
00006 //  the Free Software Foundation, either version 3 of the License, or
00007 //  (at your option) any later version.
00008 //
00009 //  This program is distributed in the hope that it will be useful,
00010 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 //  GNU General Public License for more details.
00013 //
00014 //  You should have received a copy of the GNU General Public License
00015 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
00016 
00017 #include "audio/sound_manager.hpp"
00018 
00019 #include <SDL.h>
00020 #include <assert.h>
00021 #include <stdexcept>
00022 #include <sstream>
00023 #include <memory>
00024 
00025 #include "audio/dummy_sound_source.hpp"
00026 #include "audio/sound_file.hpp"
00027 #include "audio/stream_sound_source.hpp"
00028 #include "util/log.hpp"
00029 
00030 SoundManager::SoundManager() :
00031   device(0), 
00032   context(0), 
00033   sound_enabled(false), 
00034   buffers(),
00035   sources(),
00036   update_list(),
00037   music_source(0),
00038   music_enabled(false),
00039   current_music()
00040 {
00041   try {
00042     device = alcOpenDevice(0);
00043     if (device == NULL) {
00044       throw std::runtime_error("Couldn't open audio device.");
00045     }
00046 
00047     int attributes[] = { 0 };
00048     context = alcCreateContext(device, attributes);
00049     check_alc_error("Couldn't create audio context: ");
00050     alcMakeContextCurrent(context);
00051     check_alc_error("Couldn't select audio context: ");
00052 
00053     check_al_error("Audio error after init: ");
00054     sound_enabled = true;
00055     music_enabled = true;
00056   } catch(std::exception& e) {
00057     if(context != NULL) {
00058       alcDestroyContext(context);
00059       context = NULL; 
00060     }
00061     if(device != NULL) {
00062       alcCloseDevice(device);
00063       device = NULL;
00064     }
00065     log_warning << "Couldn't initialize audio device: " << e.what() << std::endl;
00066     print_openal_version();
00067   }
00068 }
00069 
00070 SoundManager::~SoundManager()
00071 {
00072   delete music_source;
00073 
00074   for(SoundSources::iterator i = sources.begin(); i != sources.end(); ++i) {
00075     delete *i;
00076   }
00077 
00078   for(SoundBuffers::iterator i = buffers.begin(); i != buffers.end(); ++i) {
00079     ALuint buffer = i->second;
00080     alDeleteBuffers(1, &buffer);
00081   }
00082 
00083   if(context != NULL) {
00084     alcDestroyContext(context);
00085     context = NULL;
00086   }
00087   if(device != NULL) {
00088     alcCloseDevice(device);
00089     device = NULL;
00090   }
00091 }
00092 
00093 ALuint
00094 SoundManager::load_file_into_buffer(SoundFile* file)
00095 {
00096   ALenum format = get_sample_format(file);
00097   ALuint buffer;
00098   alGenBuffers(1, &buffer);
00099   check_al_error("Couldn't create audio buffer: ");
00100   char* samples = new char[file->size];
00101   try {
00102     file->read(samples, file->size);
00103     alBufferData(buffer, format, samples,
00104                  static_cast<ALsizei> (file->size),
00105                  static_cast<ALsizei> (file->rate));
00106     check_al_error("Couldn't fill audio buffer: ");
00107   } catch(...) {
00108     delete[] samples;
00109     throw;
00110   }
00111   delete[] samples;
00112 
00113   return buffer;
00114 }
00115 
00116 OpenALSoundSource*
00117 SoundManager::intern_create_sound_source(const std::string& filename)
00118 {
00119   assert(sound_enabled);
00120 
00121   std::auto_ptr<OpenALSoundSource> source (new OpenALSoundSource());
00122 
00123   ALuint buffer;
00124 
00125   // reuse an existing static sound buffer
00126   SoundBuffers::iterator i = buffers.find(filename);
00127   if(i != buffers.end()) {
00128     buffer = i->second;
00129   } else {
00130     // Load sound file
00131     std::auto_ptr<SoundFile> file (load_sound_file(filename));
00132 
00133     if(file->size < 100000) {
00134       buffer = load_file_into_buffer(file.get());
00135       buffers.insert(std::make_pair(filename, buffer));
00136     } else {
00137       StreamSoundSource* source = new StreamSoundSource();
00138       source->set_sound_file(file.release());
00139       return source;
00140     }
00141 
00142     log_debug << "Uncached sound \"" << filename << "\" requested to be played" << std::endl;
00143   }
00144 
00145   alSourcei(source->source, AL_BUFFER, buffer);
00146   return source.release();
00147 }
00148 
00149 SoundSource*
00150 SoundManager::create_sound_source(const std::string& filename)
00151 {
00152   if(!sound_enabled)
00153     return create_dummy_sound_source();
00154 
00155   try {
00156     return intern_create_sound_source(filename);
00157   } catch(std::exception &e) {
00158     log_warning << "Couldn't create audio source: " << e.what() << std::endl;
00159     return create_dummy_sound_source();
00160   }
00161 }
00162 
00163 void
00164 SoundManager::preload(const std::string& filename)
00165 {
00166   if(!sound_enabled)
00167     return;
00168 
00169   SoundBuffers::iterator i = buffers.find(filename);
00170   // already loaded?
00171   if(i != buffers.end())
00172     return;
00173   try {
00174     std::auto_ptr<SoundFile> file (load_sound_file(filename));
00175     // only keep small files
00176     if(file->size >= 100000)
00177       return;
00178 
00179     ALuint buffer = load_file_into_buffer(file.get());
00180     buffers.insert(std::make_pair(filename, buffer));
00181   } catch(std::exception& e) {
00182     log_warning << "Error while preloading sound file: " << e.what() << std::endl;
00183   }
00184 }
00185 
00186 void
00187 SoundManager::play(const std::string& filename, const Vector& pos)
00188 {
00189   if(!sound_enabled)
00190     return;
00191 
00192   try {
00193     std::auto_ptr<OpenALSoundSource> source
00194       (intern_create_sound_source(filename));
00195 
00196     if(pos.x < 0 || pos.y < 0) {
00197       source->set_relative(true);
00198     } else {
00199       source->set_position(pos);
00200     }
00201     source->play();
00202     sources.push_back(source.release());
00203   } catch(std::exception& e) {
00204     log_warning << "Couldn't play sound " << filename << ": " << e.what() << std::endl;
00205   }
00206 }
00207 
00208 void
00209 SoundManager::manage_source(SoundSource* source)
00210 {
00211   assert(source != NULL);
00212 
00213   OpenALSoundSource* openal_source = dynamic_cast<OpenALSoundSource*> (source);
00214   if(openal_source != NULL) {
00215     sources.push_back(openal_source);
00216   }
00217 }
00218 
00219 void
00220 SoundManager::register_for_update( StreamSoundSource* sss ){
00221   if( sss != NULL ){
00222     update_list.push_back( sss );
00223   }
00224 }
00225 
00226 void
00227 SoundManager::remove_from_update( StreamSoundSource* sss  ){
00228   if( sss != NULL ){
00229     StreamSoundSources::iterator i = update_list.begin();
00230     while( i != update_list.end() ){
00231       if( *i == sss ){
00232         i = update_list.erase(i);
00233       } else {
00234         i++;
00235       }
00236     }
00237   }
00238 }
00239 
00240 void
00241 SoundManager::enable_sound(bool enable)
00242 {
00243   if(device == NULL)
00244     return;
00245 
00246   sound_enabled = enable;
00247 }
00248 
00249 void
00250 SoundManager::enable_music(bool enable)
00251 {
00252   if(device == NULL)
00253     return;
00254 
00255   music_enabled = enable;
00256   if(music_enabled) {
00257     play_music(current_music);
00258   } else {
00259     if(music_source) {
00260       delete music_source;
00261       music_source = NULL;
00262     }
00263   }
00264 }
00265 
00266 void
00267 SoundManager::stop_music(float fadetime)
00268 {
00269   if(fadetime > 0) {
00270     if(music_source
00271        && music_source->get_fade_state() != StreamSoundSource::FadingOff)
00272       music_source->set_fading(StreamSoundSource::FadingOff, fadetime);
00273   } else {
00274     delete music_source;
00275     music_source = NULL;
00276   }
00277   current_music = "";
00278 }
00279 
00280 void
00281 SoundManager::play_music(const std::string& filename, bool fade)
00282 {
00283   if(filename == current_music && music_source != NULL)
00284     return;
00285   current_music = filename;
00286   if(!music_enabled)
00287     return;
00288 
00289   if(filename == "") {
00290     delete music_source;
00291     music_source = NULL;
00292     return;
00293   }
00294 
00295   try {
00296     std::auto_ptr<StreamSoundSource> newmusic (new StreamSoundSource());
00297     newmusic->set_sound_file(load_sound_file(filename));
00298     newmusic->set_looping(true);
00299     newmusic->set_relative(true);
00300     if(fade)
00301       newmusic->set_fading(StreamSoundSource::FadingOn, .5f);
00302     newmusic->play();
00303 
00304     delete music_source;
00305     music_source = newmusic.release();
00306   } catch(std::exception& e) {
00307     log_warning << "Couldn't play music file '" << filename << "': " << e.what() << std::endl;
00308   }
00309 }
00310 
00311 void
00312 SoundManager::set_listener_position(const Vector& pos)
00313 {
00314   static Uint32 lastticks = SDL_GetTicks();
00315 
00316   Uint32 current_ticks = SDL_GetTicks();
00317   if(current_ticks - lastticks < 300)
00318     return;
00319   lastticks = current_ticks;
00320 
00321   alListener3f(AL_POSITION, pos.x, pos.y, 0);
00322 }
00323 
00324 void
00325 SoundManager::set_listener_velocity(const Vector& vel)
00326 {
00327   alListener3f(AL_VELOCITY, vel.x, vel.y, 0);
00328 }
00329 
00330 void
00331 SoundManager::update()
00332 {
00333   static Uint32 lasttime = SDL_GetTicks();
00334   Uint32 now = SDL_GetTicks();
00335 
00336   if(now - lasttime < 300)
00337     return;
00338   lasttime = now;
00339 
00340   // update and check for finished sound sources
00341   for(SoundSources::iterator i = sources.begin(); i != sources.end(); ) {
00342     OpenALSoundSource* source = *i;
00343 
00344     source->update();
00345 
00346     if(!source->playing()) {
00347       delete source;
00348       i = sources.erase(i);
00349     } else {
00350       ++i;
00351     }
00352   }
00353   // check streaming sounds
00354   if(music_source) {
00355     music_source->update();
00356   }
00357 
00358   if (context)
00359   {
00360     alcProcessContext(context);
00361     check_alc_error("Error while processing audio context: ");
00362   }
00363 
00364   //run update() for stream_sound_source
00365   StreamSoundSources::iterator s = update_list.begin();
00366   while( s != update_list.end() ){
00367     (*s)->update();
00368     s++;
00369   }
00370 }
00371 
00372 ALenum
00373 SoundManager::get_sample_format(SoundFile* file)
00374 {
00375   if(file->channels == 2) {
00376     if(file->bits_per_sample == 16) {
00377       return AL_FORMAT_STEREO16;
00378     } else if(file->bits_per_sample == 8) {
00379       return AL_FORMAT_STEREO8;
00380     } else {
00381       throw std::runtime_error("Only 16 and 8 bit samples supported");
00382     }
00383   } else if(file->channels == 1) {
00384     if(file->bits_per_sample == 16) {
00385       return AL_FORMAT_MONO16;
00386     } else if(file->bits_per_sample == 8) {
00387       return AL_FORMAT_MONO8;
00388     } else {
00389       throw std::runtime_error("Only 16 and 8 bit samples supported");
00390     }
00391   }
00392 
00393   throw std::runtime_error("Only 1 and 2 channel samples supported");
00394 }
00395 
00396 void
00397 SoundManager::print_openal_version()
00398 {
00399   log_info << "OpenAL Vendor: " << alGetString(AL_VENDOR) << std::endl;
00400   log_info << "OpenAL Version: " << alGetString(AL_VERSION) << std::endl;
00401   log_info << "OpenAL Renderer: " << alGetString(AL_RENDERER) << std::endl;
00402   log_info << "OpenAl Extensions: " << alGetString(AL_EXTENSIONS) << std::endl;
00403 }
00404 
00405 void
00406 SoundManager::check_alc_error(const char* message)
00407 {
00408   int err = alcGetError(device);
00409   if(err != ALC_NO_ERROR) {
00410     std::stringstream msg;
00411     msg << message << alcGetString(device, err);
00412     throw std::runtime_error(msg.str());
00413   }
00414 }
00415 
00416 void
00417 SoundManager::check_al_error(const char* message)
00418 {
00419   int err = alGetError();
00420   if(err != AL_NO_ERROR) {
00421     std::stringstream msg;
00422     msg << message << alGetString(err);
00423     throw std::runtime_error(msg.str());
00424   }
00425 }
00426 
00427 /* EOF */

Generated on Mon Jun 9 03:38:16 2014 for SuperTux by  doxygen 1.5.1