2 // Copyright (C) 2006 Matthias Braun <matze@braunis.de>
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 #include "audio/sound_manager.hpp"
25 #include "audio/dummy_sound_source.hpp"
26 #include "audio/sound_file.hpp"
27 #include "audio/stream_sound_source.hpp"
28 #include "util/log.hpp"
31 /** Older openal versions often miss this function and it isn't that vital for
37 #define alcGetString(x,y) ""
40 SoundManager* sound_manager = 0;
42 SoundManager::SoundManager() :
54 device = alcOpenDevice(0);
56 throw std::runtime_error("Couldn't open audio device.");
59 int attributes[] = { 0 };
60 context = alcCreateContext(device, attributes);
61 check_alc_error("Couldn't create audio context: ");
62 alcMakeContextCurrent(context);
63 check_alc_error("Couldn't select audio context: ");
65 check_al_error("Audio error after init: ");
68 } catch(std::exception& e) {
70 alcDestroyContext(context);
73 alcCloseDevice(device);
75 log_warning << "Couldn't initialize audio device: " << e.what() << std::endl;
76 print_openal_version();
80 SoundManager::~SoundManager()
84 for(SoundSources::iterator i = sources.begin(); i != sources.end(); ++i) {
88 for(SoundBuffers::iterator i = buffers.begin(); i != buffers.end(); ++i) {
89 ALuint buffer = i->second;
90 alDeleteBuffers(1, &buffer);
94 alcDestroyContext(context);
97 alcCloseDevice(device);
102 SoundManager::load_file_into_buffer(SoundFile* file)
104 ALenum format = get_sample_format(file);
106 alGenBuffers(1, &buffer);
107 check_al_error("Couldn't create audio buffer: ");
108 char* samples = new char[file->size];
110 file->read(samples, file->size);
111 alBufferData(buffer, format, samples,
112 static_cast<ALsizei> (file->size),
113 static_cast<ALsizei> (file->rate));
114 check_al_error("Couldn't fill audio buffer: ");
125 SoundManager::intern_create_sound_source(const std::string& filename)
128 throw std::runtime_error("sound disabled");
130 std::auto_ptr<OpenALSoundSource> source (new OpenALSoundSource());
134 // reuse an existing static sound buffer
135 SoundBuffers::iterator i = buffers.find(filename);
136 if(i != buffers.end()) {
140 std::auto_ptr<SoundFile> file (load_sound_file(filename));
142 if(file->size < 100000) {
143 buffer = load_file_into_buffer(file.get());
144 buffers.insert(std::make_pair(filename, buffer));
146 StreamSoundSource* source = new StreamSoundSource();
147 source->set_sound_file(file.release());
151 log_debug << "Uncached sound \"" << filename << "\" requested to be played" << std::endl;
154 alSourcei(source->source, AL_BUFFER, buffer);
155 return source.release();
159 SoundManager::create_sound_source(const std::string& filename)
162 return create_dummy_sound_source();
165 return intern_create_sound_source(filename);
166 } catch(std::exception &e) {
167 log_warning << "Couldn't create audio source: " << e.what() << std::endl;
168 return create_dummy_sound_source();
173 SoundManager::preload(const std::string& filename)
178 SoundBuffers::iterator i = buffers.find(filename);
180 if(i != buffers.end())
183 std::auto_ptr<SoundFile> file (load_sound_file(filename));
184 // only keep small files
185 if(file->size >= 100000)
188 ALuint buffer = load_file_into_buffer(file.get());
189 buffers.insert(std::make_pair(filename, buffer));
190 } catch(std::exception& e) {
191 log_warning << "Error while preloading sound file: " << e.what() << std::endl;
196 SoundManager::play(const std::string& filename, const Vector& pos)
202 std::auto_ptr<OpenALSoundSource> source
203 (intern_create_sound_source(filename));
205 if(pos == Vector(-1, -1)) {
206 source->set_rollof_factor(0);
208 source->set_position(pos);
211 sources.push_back(source.release());
212 } catch(std::exception& e) {
213 log_warning << "Couldn't play sound " << filename << ": " << e.what() << std::endl;
218 SoundManager::manage_source(SoundSource* source)
220 assert(source != NULL);
222 OpenALSoundSource* openal_source = dynamic_cast<OpenALSoundSource*> (source);
223 if(openal_source != NULL) {
224 sources.push_back(openal_source);
229 SoundManager::register_for_update( StreamSoundSource* sss ){
231 update_list.push_back( sss );
236 SoundManager::remove_from_update( StreamSoundSource* sss ){
238 StreamSoundSources::iterator i = update_list.begin();
239 while( i != update_list.end() ){
241 i = update_list.erase(i);
250 SoundManager::enable_sound(bool enable)
255 sound_enabled = enable;
259 SoundManager::enable_music(bool enable)
264 music_enabled = enable;
266 play_music(current_music);
276 SoundManager::stop_music(float fadetime)
280 && music_source->get_fade_state() != StreamSoundSource::FadingOff)
281 music_source->set_fading(StreamSoundSource::FadingOff, fadetime);
290 SoundManager::play_music(const std::string& filename, bool fade)
292 if(filename == current_music && music_source != NULL)
294 current_music = filename;
305 std::auto_ptr<StreamSoundSource> newmusic (new StreamSoundSource());
306 alSourcef(newmusic->source, AL_ROLLOFF_FACTOR, 0);
307 newmusic->set_sound_file(load_sound_file(filename));
308 newmusic->set_looping(true);
310 newmusic->set_fading(StreamSoundSource::FadingOn, .5f);
314 music_source = newmusic.release();
315 } catch(std::exception& e) {
316 log_warning << "Couldn't play music file '" << filename << "': " << e.what() << std::endl;
321 SoundManager::set_listener_position(const Vector& pos)
323 static Uint32 lastticks = SDL_GetTicks();
325 Uint32 current_ticks = SDL_GetTicks();
326 if(current_ticks - lastticks < 300)
328 lastticks = current_ticks;
330 alListener3f(AL_POSITION, pos.x, pos.y, 0);
334 SoundManager::set_listener_velocity(const Vector& vel)
336 alListener3f(AL_VELOCITY, vel.x, vel.y, 0);
340 SoundManager::update()
342 static Uint32 lasttime = SDL_GetTicks();
343 Uint32 now = SDL_GetTicks();
345 if(now - lasttime < 300)
349 // update and check for finished sound sources
350 for(SoundSources::iterator i = sources.begin(); i != sources.end(); ) {
351 OpenALSoundSource* source = *i;
355 if(!source->playing()) {
357 i = sources.erase(i);
362 // check streaming sounds
364 music_source->update();
369 alcProcessContext(context);
370 check_alc_error("Error while processing audio context: ");
373 //run update() for stream_sound_source
374 StreamSoundSources::iterator s = update_list.begin();
375 while( s != update_list.end() ){
382 SoundManager::get_sample_format(SoundFile* file)
384 if(file->channels == 2) {
385 if(file->bits_per_sample == 16) {
386 return AL_FORMAT_STEREO16;
387 } else if(file->bits_per_sample == 8) {
388 return AL_FORMAT_STEREO8;
390 throw std::runtime_error("Only 16 and 8 bit samples supported");
392 } else if(file->channels == 1) {
393 if(file->bits_per_sample == 16) {
394 return AL_FORMAT_MONO16;
395 } else if(file->bits_per_sample == 8) {
396 return AL_FORMAT_MONO8;
398 throw std::runtime_error("Only 16 and 8 bit samples supported");
402 throw std::runtime_error("Only 1 and 2 channel samples supported");
406 SoundManager::print_openal_version()
408 log_info << "OpenAL Vendor: " << alGetString(AL_VENDOR) << std::endl;
409 log_info << "OpenAL Version: " << alGetString(AL_VERSION) << std::endl;
410 log_info << "OpenAL Renderer: " << alGetString(AL_RENDERER) << std::endl;
411 log_info << "OpenAl Extensions: " << alGetString(AL_EXTENSIONS) << std::endl;
415 SoundManager::check_alc_error(const char* message)
417 int err = alcGetError(device);
418 if(err != ALC_NO_ERROR) {
419 std::stringstream msg;
420 msg << message << alcGetString(device, err);
421 throw std::runtime_error(msg.str());
426 SoundManager::check_al_error(const char* message)
428 int err = alGetError();
429 if(err != AL_NO_ERROR) {
430 std::stringstream msg;
431 msg << message << alGetString(err);
432 throw std::runtime_error(msg.str());