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