catch exceptions when we can't create new audio sources
[supertux.git] / src / audio / sound_manager.cpp
1 //  $Id$
2 //
3 //  SuperTux
4 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
5 //
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.
10 //
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.
15 //
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.
19 #include <config.h>
20
21 #include "sound_manager.hpp"
22
23 #include <stdexcept>
24 #include <iostream>
25 #include <sstream>
26 #include <memory>
27 #include <assert.h>
28
29 #include "sound_file.hpp"
30 #include "sound_source.hpp"
31 #include "openal_sound_source.hpp"
32 #include "stream_sound_source.hpp"
33 #include "dummy_sound_source.hpp"
34 #include "log.hpp"
35 #include "timer.hpp"
36
37 SoundManager* sound_manager = 0;
38
39 SoundManager::SoundManager()
40   : device(0), context(0), sound_enabled(false), music_source(0),
41     music_enabled(false)
42 {
43   try {
44     device = alcOpenDevice(0);
45     if (device == NULL) {
46       throw std::runtime_error("Couldn't open audio device.");
47     }
48
49     int attributes[] = { 0 };
50     context = alcCreateContext(device, attributes);
51     check_alc_error("Couldn't create audio context: ");
52     alcMakeContextCurrent(context);
53     check_alc_error("Couldn't select audio context: ");
54
55     check_al_error("Audio error after init: ");
56     sound_enabled = true;
57     music_enabled = true;
58   } catch(std::exception& e) {
59     if(context != NULL)
60       alcDestroyContext(context);
61     context = NULL;
62     if(device != NULL)
63       alcCloseDevice(device);
64     device = NULL;
65     log_warning << "Couldn't initialize audio device: " << e.what() << std::endl;
66     print_openal_version();
67   }
68 }
69
70 SoundManager::~SoundManager()
71 {
72   delete music_source;
73
74   for(SoundSources::iterator i = sources.begin(); i != sources.end(); ++i) {
75     delete *i;
76   }
77
78   for(SoundBuffers::iterator i = buffers.begin(); i != buffers.end(); ++i) {
79     ALuint buffer = i->second;
80     alDeleteBuffers(1, &buffer);
81   }
82
83   if(context != NULL) {
84     alcDestroyContext(context);
85   }
86   if(device != NULL) {
87     alcCloseDevice(device);
88   }
89 }
90
91 ALuint
92 SoundManager::load_file_into_buffer(SoundFile* file)
93 {
94   ALenum format = get_sample_format(file);
95   ALuint buffer;
96   alGenBuffers(1, &buffer);
97   check_al_error("Couldn't create audio buffer: ");
98   char* samples = new char[file->size];
99   try {
100     file->read(samples, file->size);
101     alBufferData(buffer, format, samples,
102         static_cast<ALsizei> (file->size),
103         static_cast<ALsizei> (file->rate));
104     check_al_error("Couldn't fill audio buffer: ");
105   } catch(...) {
106     delete[] samples;
107     throw;
108   }
109   delete[] samples;
110
111   return buffer;
112 }
113
114 SoundSource*
115 SoundManager::create_sound_source(const std::string& filename)
116 {
117   if(!sound_enabled)
118     return create_dummy_sound_source();
119
120   std::auto_ptr<OpenALSoundSource> source;
121   try {
122     source.reset(new OpenALSoundSource());
123   } catch(std::exception& e) {
124     log_warning << "Couldn't create audio source: " << e.what() << std::endl;
125     return create_dummy_sound_source();
126   }
127
128   ALuint buffer;
129
130   // reuse an existing static sound buffer
131   SoundBuffers::iterator i = buffers.find(filename);
132   if(i != buffers.end()) {
133     buffer = i->second;
134   } else {
135     try {
136       // Load sound file
137       std::auto_ptr<SoundFile> file (load_sound_file(filename));
138
139       if(file->size < 100000) {
140         buffer = load_file_into_buffer(file.get());
141         buffers.insert(std::make_pair(filename, buffer));
142       } else {
143         StreamSoundSource* source = new StreamSoundSource();
144         source->set_sound_file(file.release());
145         return source;
146       }
147     } catch(std::exception& e) {
148       log_warning << "Couldn't load soundfile '" << filename << "': " << e.what() << std::endl;
149       return create_dummy_sound_source();
150     }
151   }
152
153   alSourcei(source->source, AL_BUFFER, buffer);
154   return source.release();
155 }
156
157 void
158 SoundManager::preload(const std::string& filename)
159 {
160   if(!sound_enabled)
161     return;
162
163   SoundBuffers::iterator i = buffers.find(filename);
164   // already loaded?
165   if(i != buffers.end())
166     return;
167
168   std::auto_ptr<SoundFile> file (load_sound_file(filename));
169   // only keep small files
170   if(file->size >= 100000)
171     return;
172
173   ALuint buffer = load_file_into_buffer(file.get());
174   buffers.insert(std::make_pair(filename, buffer));
175 }
176
177 void
178 SoundManager::play(const std::string& filename, const Vector& pos)
179 {
180   if(!sound_enabled)
181     return;
182
183   try {
184     std::auto_ptr<OpenALSoundSource> source
185       (static_cast<OpenALSoundSource*> (create_sound_source(filename)));
186
187     if(pos == Vector(-1, -1)) {
188       source->set_rollof_factor(0);
189     } else {
190       source->set_position(pos);
191     }
192     source->play();
193     sources.push_back(source.release());
194   } catch(std::exception& e) {
195     log_warning << "Couldn't play sound " << filename << ": " << e.what() << std::endl;
196   }
197 }
198
199 void
200 SoundManager::manage_source(SoundSource* source)
201 {
202   assert(source != NULL);
203
204   OpenALSoundSource* openal_source = dynamic_cast<OpenALSoundSource*> (source);
205   if(openal_source != NULL) {
206     sources.push_back(openal_source);
207   }
208 }
209
210 void
211 SoundManager::register_for_update( StreamSoundSource* sss ){
212   if( sss != NULL ){
213     update_list.push_back( sss );
214   }
215 }
216
217 void
218 SoundManager::remove_from_update( StreamSoundSource* sss  ){
219   if( sss != NULL ){
220     StreamSoundSources::iterator i = update_list.begin();
221         while( i != update_list.end() ){
222       if( *i == sss ){
223         i = update_list.erase(i);
224       } else {
225         i++;
226       }
227     }
228   }
229 }
230
231 void
232 SoundManager::enable_sound(bool enable)
233 {
234   if(device == NULL)
235     return;
236
237   sound_enabled = enable;
238 }
239
240 void
241 SoundManager::enable_music(bool enable)
242 {
243   if(device == NULL)
244     return;
245
246   music_enabled = enable;
247   if(music_enabled) {
248     play_music(current_music);
249   } else {
250     if(music_source) {
251       delete music_source;
252       music_source = 0;
253     }
254   }
255 }
256
257 void
258 SoundManager::stop_music(float fadetime)
259 {
260   if(fadetime > 0) {
261     if(music_source
262         && music_source->get_fade_state() != StreamSoundSource::FadingOff)
263       music_source->set_fading(StreamSoundSource::FadingOff, fadetime);
264   } else {
265     delete music_source;
266     music_source = NULL;
267   }
268   current_music = "";
269 }
270
271 void
272 SoundManager::play_music(const std::string& filename, bool fade)
273 {
274   if(filename == current_music && music_source != NULL)
275     return;
276   current_music = filename;
277   if(!music_enabled)
278     return;
279
280   if(filename == "") {
281     delete music_source;
282     music_source = NULL;
283     return;
284   }
285
286   try {
287     std::auto_ptr<StreamSoundSource> newmusic (new StreamSoundSource());
288     alSourcef(newmusic->source, AL_ROLLOFF_FACTOR, 0);
289     newmusic->set_sound_file(load_sound_file(filename));
290     newmusic->set_looping(true);
291     if(fade)
292       newmusic->set_fading(StreamSoundSource::FadingOn, .5f);
293     newmusic->play();
294
295     delete music_source;
296     music_source = newmusic.release();
297   } catch(std::exception& e) {
298     log_warning << "Couldn't play music file '" << filename << "': " << e.what() << std::endl;
299   }
300 }
301
302 void
303 SoundManager::set_listener_position(const Vector& pos)
304 {
305   static Uint32 lastticks = 0;
306
307   Uint32 current_ticks = SDL_GetTicks();
308   if(current_ticks - lastticks < 300)
309     return;
310   lastticks = current_ticks;
311
312   alListener3f(AL_POSITION, pos.x, pos.y, 0);
313 }
314
315 void
316 SoundManager::set_listener_velocity(const Vector& vel)
317 {
318   alListener3f(AL_VELOCITY, vel.x, vel.y, 0);
319 }
320
321 void
322 SoundManager::update()
323 {
324   static float lasttime = real_time;
325
326   if(real_time - lasttime < 0.3)
327     return;
328   lasttime = real_time;
329
330   // update and check for finished sound sources
331   for(SoundSources::iterator i = sources.begin(); i != sources.end(); ) {
332     OpenALSoundSource* source = *i;
333
334     source->update();
335
336     if(!source->playing()) {
337       delete source;
338       i = sources.erase(i);
339     } else {
340       ++i;
341     }
342   }
343   // check streaming sounds
344   if(music_source) {
345     music_source->update();
346   }
347
348   if (context)
349   {
350     alcProcessContext(context);
351     check_alc_error("Error while processing audio context: ");
352   }
353
354   //run update() for stream_sound_source
355   StreamSoundSources::iterator s = update_list.begin();
356   while( s != update_list.end() ){
357     (*s)->update();
358     s++;
359   }
360 }
361
362 ALenum
363 SoundManager::get_sample_format(SoundFile* file)
364 {
365   if(file->channels == 2) {
366     if(file->bits_per_sample == 16) {
367       return AL_FORMAT_STEREO16;
368     } else if(file->bits_per_sample == 8) {
369       return AL_FORMAT_STEREO8;
370     } else {
371       throw std::runtime_error("Only 16 and 8 bit samples supported");
372     }
373   } else if(file->channels == 1) {
374     if(file->bits_per_sample == 16) {
375       return AL_FORMAT_MONO16;
376     } else if(file->bits_per_sample == 8) {
377       return AL_FORMAT_MONO8;
378     } else {
379       throw std::runtime_error("Only 16 and 8 bit samples supported");
380     }
381   }
382
383   throw std::runtime_error("Only 1 and 2 channel samples supported");
384 }
385
386 void
387 SoundManager::print_openal_version()
388 {
389   log_info << "OpenAL Vendor: " << alGetString(AL_VENDOR) << std::endl;
390   log_info << "OpenAL Version: " << alGetString(AL_VERSION) << std::endl;
391   log_info << "OpenAL Renderer: " << alGetString(AL_RENDERER) << std::endl;
392   log_info << "OpenAl Extensions: " << alGetString(AL_EXTENSIONS) << std::endl;
393 }
394
395 void
396 SoundManager::check_alc_error(const char* message)
397 {
398   int err = alcGetError(device);
399   if(err != ALC_NO_ERROR) {
400     std::stringstream msg;
401     msg << message << alcGetString(device, err);
402     throw std::runtime_error(msg.str());
403   }
404 }
405
406 void
407 SoundManager::check_al_error(const char* message)
408 {
409   int err = alGetError();
410   if(err != AL_NO_ERROR) {
411     std::stringstream msg;
412     msg << message << alGetString(err);
413     throw std::runtime_error(msg.str());
414   }
415 }