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"
30 SoundManager::SoundManager() :
42 device = alcOpenDevice(0);
44 throw std::runtime_error("Couldn't open audio device.");
47 int attributes[] = { 0 };
48 context = alcCreateContext(device, attributes);
49 check_alc_error("Couldn't create audio context: ");
50 alcMakeContextCurrent(context);
51 check_alc_error("Couldn't select audio context: ");
53 check_al_error("Audio error after init: ");
56 } catch(std::exception& e) {
58 alcDestroyContext(context);
62 alcCloseDevice(device);
65 log_warning << "Couldn't initialize audio device: " << e.what() << std::endl;
66 print_openal_version();
70 SoundManager::~SoundManager()
75 for(SoundBuffers::iterator i = buffers.begin(); i != buffers.end(); ++i) {
76 ALuint buffer = i->second;
77 alDeleteBuffers(1, &buffer);
81 alcDestroyContext(context);
85 alcCloseDevice(device);
91 SoundManager::load_file_into_buffer(SoundFile& file)
93 ALenum format = get_sample_format(file);
95 alGenBuffers(1, &buffer);
96 check_al_error("Couldn't create audio buffer: ");
97 std::unique_ptr<char[]> samples(new char[file.size]);
98 file.read(samples.get(), file.size);
99 alBufferData(buffer, format, samples.get(),
100 static_cast<ALsizei>(file.size),
101 static_cast<ALsizei>(file.rate));
102 check_al_error("Couldn't fill audio buffer: ");
107 std::unique_ptr<OpenALSoundSource>
108 SoundManager::intern_create_sound_source(const std::string& filename)
110 assert(sound_enabled);
112 std::unique_ptr<OpenALSoundSource> source(new OpenALSoundSource);
116 // reuse an existing static sound buffer
117 SoundBuffers::iterator i = buffers.find(filename);
118 if(i != buffers.end()) {
122 std::unique_ptr<SoundFile> file(load_sound_file(filename));
124 if(file->size < 100000) {
125 buffer = load_file_into_buffer(*file);
126 buffers.insert(std::make_pair(filename, buffer));
128 std::unique_ptr<StreamSoundSource> source(new StreamSoundSource);
129 source->set_sound_file(std::move(file));
130 return std::move(source);
133 log_debug << "Uncached sound \"" << filename << "\" requested to be played" << std::endl;
136 alSourcei(source->source, AL_BUFFER, buffer);
137 return std::move(source);
140 std::unique_ptr<SoundSource>
141 SoundManager::create_sound_source(const std::string& filename)
144 return create_dummy_sound_source();
147 return intern_create_sound_source(filename);
148 } catch(std::exception &e) {
149 log_warning << "Couldn't create audio source: " << e.what() << std::endl;
150 return create_dummy_sound_source();
155 SoundManager::preload(const std::string& filename)
160 SoundBuffers::iterator i = buffers.find(filename);
162 if(i != buffers.end())
165 std::unique_ptr<SoundFile> file (load_sound_file(filename));
166 // only keep small files
167 if(file->size >= 100000)
170 ALuint buffer = load_file_into_buffer(*file);
171 buffers.insert(std::make_pair(filename, buffer));
172 } catch(std::exception& e) {
173 log_warning << "Error while preloading sound file: " << e.what() << std::endl;
178 SoundManager::play(const std::string& filename, const Vector& pos)
184 std::unique_ptr<OpenALSoundSource> source(intern_create_sound_source(filename));
186 if(pos.x < 0 || pos.y < 0) {
187 source->set_relative(true);
189 source->set_position(pos);
192 sources.push_back(std::move(source));
193 } catch(std::exception& e) {
194 log_warning << "Couldn't play sound " << filename << ": " << e.what() << std::endl;
199 SoundManager::manage_source(std::unique_ptr<SoundSource> source)
202 if (dynamic_cast<OpenALSoundSource*>(source.get()))
204 std::unique_ptr<OpenALSoundSource> openal_source(dynamic_cast<OpenALSoundSource*>(source.release()));
205 sources.push_back(std::move(openal_source));
210 SoundManager::register_for_update(StreamSoundSource* sss)
214 update_list.push_back(sss);
219 SoundManager::remove_from_update(StreamSoundSource* sss)
223 StreamSoundSources::iterator i = update_list.begin();
224 while( i != update_list.end() ){
226 i = update_list.erase(i);
235 SoundManager::enable_sound(bool enable)
240 sound_enabled = enable;
244 SoundManager::enable_music(bool enable)
249 music_enabled = enable;
251 play_music(current_music);
254 music_source.reset();
260 SoundManager::stop_music(float fadetime)
264 && music_source->get_fade_state() != StreamSoundSource::FadingOff)
265 music_source->set_fading(StreamSoundSource::FadingOff, fadetime);
267 music_source.reset();
273 SoundManager::play_music(const std::string& filename, bool fade)
275 if(filename == current_music && music_source != NULL)
277 current_music = filename;
282 music_source.reset();
287 std::unique_ptr<StreamSoundSource> newmusic (new StreamSoundSource());
288 newmusic->set_sound_file(load_sound_file(filename));
289 newmusic->set_looping(true);
290 newmusic->set_relative(true);
292 newmusic->set_fading(StreamSoundSource::FadingOn, .5f);
295 music_source = std::move(newmusic);
296 } catch(std::exception& e) {
297 log_warning << "Couldn't play music file '" << filename << "': " << e.what() << std::endl;
298 // When this happens, previous music continued playing, stop it, just in case.
304 SoundManager::set_listener_position(const Vector& pos)
306 static Uint32 lastticks = SDL_GetTicks();
308 Uint32 current_ticks = SDL_GetTicks();
309 if(current_ticks - lastticks < 300)
311 lastticks = current_ticks;
313 alListener3f(AL_POSITION, pos.x, pos.y, 0);
317 SoundManager::set_listener_velocity(const Vector& vel)
319 alListener3f(AL_VELOCITY, vel.x, vel.y, 0);
323 SoundManager::update()
325 static Uint32 lasttime = SDL_GetTicks();
326 Uint32 now = SDL_GetTicks();
328 if(now - lasttime < 300)
332 // update and check for finished sound sources
333 for(SoundSources::iterator i = sources.begin(); i != sources.end(); ) {
338 if(!source->playing()) {
339 i = sources.erase(i);
344 // check streaming sounds
346 music_source->update();
351 alcProcessContext(context);
352 check_alc_error("Error while processing audio context: ");
355 //run update() for stream_sound_source
356 StreamSoundSources::iterator s = update_list.begin();
357 while( s != update_list.end() ){
364 SoundManager::get_sample_format(const SoundFile& file)
366 if(file.channels == 2) {
367 if(file.bits_per_sample == 16) {
368 return AL_FORMAT_STEREO16;
369 } else if(file.bits_per_sample == 8) {
370 return AL_FORMAT_STEREO8;
372 throw std::runtime_error("Only 16 and 8 bit samples supported");
374 } else if(file.channels == 1) {
375 if(file.bits_per_sample == 16) {
376 return AL_FORMAT_MONO16;
377 } else if(file.bits_per_sample == 8) {
378 return AL_FORMAT_MONO8;
380 throw std::runtime_error("Only 16 and 8 bit samples supported");
384 throw std::runtime_error("Only 1 and 2 channel samples supported");
388 SoundManager::print_openal_version()
390 log_info << "OpenAL Vendor: " << alGetString(AL_VENDOR) << std::endl;
391 log_info << "OpenAL Version: " << alGetString(AL_VERSION) << std::endl;
392 log_info << "OpenAL Renderer: " << alGetString(AL_RENDERER) << std::endl;
393 log_info << "OpenAl Extensions: " << alGetString(AL_EXTENSIONS) << std::endl;
397 SoundManager::check_alc_error(const char* message)
399 int err = alcGetError(device);
400 if(err != ALC_NO_ERROR) {
401 std::stringstream msg;
402 msg << message << alcGetString(device, err);
403 throw std::runtime_error(msg.str());
408 SoundManager::check_al_error(const char* message)
410 int err = alGetError();
411 if(err != AL_NO_ERROR) {
412 std::stringstream msg;
413 msg << message << alGetString(err);
414 throw std::runtime_error(msg.str());