4 // Copyright (C) 2006 Matthias Braun <matze@braunis.de>
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 #include "sound_manager.hpp"
30 #include "sound_file.hpp"
31 #include "sound_source.hpp"
32 #include "openal_sound_source.hpp"
33 #include "stream_sound_source.hpp"
34 #include "dummy_sound_source.hpp"
39 /** Older openal versions often miss this function and it isn't that vital for
45 #define alcGetString(x,y) ""
48 SoundManager* sound_manager = 0;
50 SoundManager::SoundManager()
51 : device(0), context(0), sound_enabled(false), music_source(0),
55 device = alcOpenDevice(0);
57 throw std::runtime_error("Couldn't open audio device.");
60 int attributes[] = { 0 };
61 context = alcCreateContext(device, attributes);
62 check_alc_error("Couldn't create audio context: ");
63 alcMakeContextCurrent(context);
64 check_alc_error("Couldn't select audio context: ");
66 check_al_error("Audio error after init: ");
69 } catch(std::exception& e) {
71 alcDestroyContext(context);
74 alcCloseDevice(device);
76 log_warning << "Couldn't initialize audio device: " << e.what() << std::endl;
77 print_openal_version();
81 SoundManager::~SoundManager()
85 for(SoundSources::iterator i = sources.begin(); i != sources.end(); ++i) {
89 for(SoundBuffers::iterator i = buffers.begin(); i != buffers.end(); ++i) {
90 ALuint buffer = i->second;
91 alDeleteBuffers(1, &buffer);
95 alcDestroyContext(context);
98 alcCloseDevice(device);
103 SoundManager::load_file_into_buffer(SoundFile* file)
105 ALenum format = get_sample_format(file);
107 alGenBuffers(1, &buffer);
108 check_al_error("Couldn't create audio buffer: ");
109 char* samples = new char[file->size];
111 file->read(samples, file->size);
112 alBufferData(buffer, format, samples,
113 static_cast<ALsizei> (file->size),
114 static_cast<ALsizei> (file->rate));
115 check_al_error("Couldn't fill audio buffer: ");
126 SoundManager::intern_create_sound_source(const std::string& filename)
129 throw std::runtime_error("sound disabled");
131 std::auto_ptr<OpenALSoundSource> source (new OpenALSoundSource());
135 // reuse an existing static sound buffer
136 SoundBuffers::iterator i = buffers.find(filename);
137 if(i != buffers.end()) {
141 std::auto_ptr<SoundFile> file (load_sound_file(filename));
143 if(file->size < 100000) {
144 buffer = load_file_into_buffer(file.get());
145 buffers.insert(std::make_pair(filename, buffer));
147 StreamSoundSource* source = new StreamSoundSource();
148 source->set_sound_file(file.release());
152 log_debug << "Uncached sound \"" << filename << "\" requested to be played" << std::endl;
155 alSourcei(source->source, AL_BUFFER, buffer);
156 return source.release();
160 SoundManager::create_sound_source(const std::string& filename)
163 return create_dummy_sound_source();
166 return intern_create_sound_source(filename);
167 } catch(std::exception &e) {
168 log_warning << "Couldn't create audio source: " << e.what() << std::endl;
169 return create_dummy_sound_source();
174 SoundManager::preload(const std::string& filename)
179 SoundBuffers::iterator i = buffers.find(filename);
181 if(i != buffers.end())
184 std::auto_ptr<SoundFile> file (load_sound_file(filename));
185 // only keep small files
186 if(file->size >= 100000)
189 ALuint buffer = load_file_into_buffer(file.get());
190 buffers.insert(std::make_pair(filename, buffer));
191 } catch(std::exception& e) {
192 log_warning << "Error while preloading sound file: " << e.what() << std::endl;
197 SoundManager::play(const std::string& filename, const Vector& pos)
203 std::auto_ptr<OpenALSoundSource> source
204 (intern_create_sound_source(filename));
206 if(pos == Vector(-1, -1)) {
207 source->set_rollof_factor(0);
209 source->set_position(pos);
212 sources.push_back(source.release());
213 } catch(std::exception& e) {
214 log_warning << "Couldn't play sound " << filename << ": " << e.what() << std::endl;
219 SoundManager::manage_source(SoundSource* source)
221 assert(source != NULL);
223 OpenALSoundSource* openal_source = dynamic_cast<OpenALSoundSource*> (source);
224 if(openal_source != NULL) {
225 sources.push_back(openal_source);
230 SoundManager::register_for_update( StreamSoundSource* sss ){
232 update_list.push_back( sss );
237 SoundManager::remove_from_update( StreamSoundSource* sss ){
239 StreamSoundSources::iterator i = update_list.begin();
240 while( i != update_list.end() ){
242 i = update_list.erase(i);
251 SoundManager::enable_sound(bool enable)
256 sound_enabled = enable;
260 SoundManager::enable_music(bool enable)
265 music_enabled = enable;
267 play_music(current_music);
277 SoundManager::stop_music(float fadetime)
281 && music_source->get_fade_state() != StreamSoundSource::FadingOff)
282 music_source->set_fading(StreamSoundSource::FadingOff, fadetime);
291 SoundManager::play_music(const std::string& filename, bool fade)
293 if(filename == current_music && music_source != NULL)
295 current_music = filename;
306 std::auto_ptr<StreamSoundSource> newmusic (new StreamSoundSource());
307 alSourcef(newmusic->source, AL_ROLLOFF_FACTOR, 0);
308 newmusic->set_sound_file(load_sound_file(filename));
309 newmusic->set_looping(true);
311 newmusic->set_fading(StreamSoundSource::FadingOn, .5f);
315 music_source = newmusic.release();
316 } catch(std::exception& e) {
317 log_warning << "Couldn't play music file '" << filename << "': " << e.what() << std::endl;
322 SoundManager::set_listener_position(const Vector& pos)
324 static Uint32 lastticks = SDL_GetTicks();
326 Uint32 current_ticks = SDL_GetTicks();
327 if(current_ticks - lastticks < 300)
329 lastticks = current_ticks;
331 alListener3f(AL_POSITION, pos.x, pos.y, 0);
335 SoundManager::set_listener_velocity(const Vector& vel)
337 alListener3f(AL_VELOCITY, vel.x, vel.y, 0);
341 SoundManager::update()
343 static Uint32 lasttime = SDL_GetTicks();
344 Uint32 now = SDL_GetTicks();
346 if(now - lasttime < 300)
350 // update and check for finished sound sources
351 for(SoundSources::iterator i = sources.begin(); i != sources.end(); ) {
352 OpenALSoundSource* source = *i;
356 if(!source->playing()) {
358 i = sources.erase(i);
363 // check streaming sounds
365 music_source->update();
370 alcProcessContext(context);
371 check_alc_error("Error while processing audio context: ");
374 //run update() for stream_sound_source
375 StreamSoundSources::iterator s = update_list.begin();
376 while( s != update_list.end() ){
383 SoundManager::get_sample_format(SoundFile* file)
385 if(file->channels == 2) {
386 if(file->bits_per_sample == 16) {
387 return AL_FORMAT_STEREO16;
388 } else if(file->bits_per_sample == 8) {
389 return AL_FORMAT_STEREO8;
391 throw std::runtime_error("Only 16 and 8 bit samples supported");
393 } else if(file->channels == 1) {
394 if(file->bits_per_sample == 16) {
395 return AL_FORMAT_MONO16;
396 } else if(file->bits_per_sample == 8) {
397 return AL_FORMAT_MONO8;
399 throw std::runtime_error("Only 16 and 8 bit samples supported");
403 throw std::runtime_error("Only 1 and 2 channel samples supported");
407 SoundManager::print_openal_version()
409 log_info << "OpenAL Vendor: " << alGetString(AL_VENDOR) << std::endl;
410 log_info << "OpenAL Version: " << alGetString(AL_VERSION) << std::endl;
411 log_info << "OpenAL Renderer: " << alGetString(AL_RENDERER) << std::endl;
412 log_info << "OpenAl Extensions: " << alGetString(AL_EXTENSIONS) << std::endl;
416 SoundManager::check_alc_error(const char* message)
418 int err = alcGetError(device);
419 if(err != ALC_NO_ERROR) {
420 std::stringstream msg;
421 msg << message << alcGetString(device, err);
422 throw std::runtime_error(msg.str());
427 SoundManager::check_al_error(const char* message)
429 int err = alGetError();
430 if(err != AL_NO_ERROR) {
431 std::stringstream msg;
432 msg << message << alGetString(err);
433 throw std::runtime_error(msg.str());