Fixed trailing whitespaces in all(?) source files of supertux, also fixed some svn...
[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
153   alSourcei(source->source, AL_BUFFER, buffer);
154   return source.release();
155 }
156
157 SoundSource*
158 SoundManager::create_sound_source(const std::string& filename)
159 {
160   if(!sound_enabled)
161     return create_dummy_sound_source();
162
163   try {
164     return intern_create_sound_source(filename);
165   } catch(std::exception &e) {
166     log_warning << "Couldn't create audio source: " << e.what() << std::endl;
167     return create_dummy_sound_source();
168   }
169 }
170
171 void
172 SoundManager::preload(const std::string& filename)
173 {
174   if(!sound_enabled)
175     return;
176
177   SoundBuffers::iterator i = buffers.find(filename);
178   // already loaded?
179   if(i != buffers.end())
180     return;
181
182   std::auto_ptr<SoundFile> file (load_sound_file(filename));
183   // only keep small files
184   if(file->size >= 100000)
185     return;
186
187   ALuint buffer = load_file_into_buffer(file.get());
188   buffers.insert(std::make_pair(filename, buffer));
189 }
190
191 void
192 SoundManager::play(const std::string& filename, const Vector& pos)
193 {
194   if(!sound_enabled)
195     return;
196
197   try {
198     std::auto_ptr<OpenALSoundSource> source
199         (intern_create_sound_source(filename));
200
201     if(pos == Vector(-1, -1)) {
202       source->set_rollof_factor(0);
203     } else {
204       source->set_position(pos);
205     }
206     source->play();
207     sources.push_back(source.release());
208   } catch(std::exception& e) {
209     log_warning << "Couldn't play sound " << filename << ": " << e.what() << std::endl;
210   }
211 }
212
213 void
214 SoundManager::manage_source(SoundSource* source)
215 {
216   assert(source != NULL);
217
218   OpenALSoundSource* openal_source = dynamic_cast<OpenALSoundSource*> (source);
219   if(openal_source != NULL) {
220     sources.push_back(openal_source);
221   }
222 }
223
224 void
225 SoundManager::register_for_update( StreamSoundSource* sss ){
226   if( sss != NULL ){
227     update_list.push_back( sss );
228   }
229 }
230
231 void
232 SoundManager::remove_from_update( StreamSoundSource* sss  ){
233   if( sss != NULL ){
234     StreamSoundSources::iterator i = update_list.begin();
235         while( i != update_list.end() ){
236       if( *i == sss ){
237         i = update_list.erase(i);
238       } else {
239         i++;
240       }
241     }
242   }
243 }
244
245 void
246 SoundManager::enable_sound(bool enable)
247 {
248   if(device == NULL)
249     return;
250
251   sound_enabled = enable;
252 }
253
254 void
255 SoundManager::enable_music(bool enable)
256 {
257   if(device == NULL)
258     return;
259
260   music_enabled = enable;
261   if(music_enabled) {
262     play_music(current_music);
263   } else {
264     if(music_source) {
265       delete music_source;
266       music_source = 0;
267     }
268   }
269 }
270
271 void
272 SoundManager::stop_music(float fadetime)
273 {
274   if(fadetime > 0) {
275     if(music_source
276         && music_source->get_fade_state() != StreamSoundSource::FadingOff)
277       music_source->set_fading(StreamSoundSource::FadingOff, fadetime);
278   } else {
279     delete music_source;
280     music_source = NULL;
281   }
282   current_music = "";
283 }
284
285 void
286 SoundManager::play_music(const std::string& filename, bool fade)
287 {
288   if(filename == current_music && music_source != NULL)
289     return;
290   current_music = filename;
291   if(!music_enabled)
292     return;
293
294   if(filename == "") {
295     delete music_source;
296     music_source = NULL;
297     return;
298   }
299
300   try {
301     std::auto_ptr<StreamSoundSource> newmusic (new StreamSoundSource());
302     alSourcef(newmusic->source, AL_ROLLOFF_FACTOR, 0);
303     newmusic->set_sound_file(load_sound_file(filename));
304     newmusic->set_looping(true);
305     if(fade)
306       newmusic->set_fading(StreamSoundSource::FadingOn, .5f);
307     newmusic->play();
308
309     delete music_source;
310     music_source = newmusic.release();
311   } catch(std::exception& e) {
312     log_warning << "Couldn't play music file '" << filename << "': " << e.what() << std::endl;
313   }
314 }
315
316 void
317 SoundManager::set_listener_position(const Vector& pos)
318 {
319   static Uint32 lastticks = SDL_GetTicks();
320
321   Uint32 current_ticks = SDL_GetTicks();
322   if(current_ticks - lastticks < 300)
323     return;
324   lastticks = current_ticks;
325
326   alListener3f(AL_POSITION, pos.x, pos.y, 0);
327 }
328
329 void
330 SoundManager::set_listener_velocity(const Vector& vel)
331 {
332   alListener3f(AL_VELOCITY, vel.x, vel.y, 0);
333 }
334
335 void
336 SoundManager::update()
337 {
338   static Uint32 lasttime = SDL_GetTicks();
339   Uint32 now = SDL_GetTicks();
340
341   if(now - lasttime < 300)
342     return;
343   lasttime = now;
344
345   // update and check for finished sound sources
346   for(SoundSources::iterator i = sources.begin(); i != sources.end(); ) {
347     OpenALSoundSource* source = *i;
348
349     source->update();
350
351     if(!source->playing()) {
352       delete source;
353       i = sources.erase(i);
354     } else {
355       ++i;
356     }
357   }
358   // check streaming sounds
359   if(music_source) {
360     music_source->update();
361   }
362
363   if (context)
364   {
365     alcProcessContext(context);
366     check_alc_error("Error while processing audio context: ");
367   }
368
369   //run update() for stream_sound_source
370   StreamSoundSources::iterator s = update_list.begin();
371   while( s != update_list.end() ){
372     (*s)->update();
373     s++;
374   }
375 }
376
377 ALenum
378 SoundManager::get_sample_format(SoundFile* file)
379 {
380   if(file->channels == 2) {
381     if(file->bits_per_sample == 16) {
382       return AL_FORMAT_STEREO16;
383     } else if(file->bits_per_sample == 8) {
384       return AL_FORMAT_STEREO8;
385     } else {
386       throw std::runtime_error("Only 16 and 8 bit samples supported");
387     }
388   } else if(file->channels == 1) {
389     if(file->bits_per_sample == 16) {
390       return AL_FORMAT_MONO16;
391     } else if(file->bits_per_sample == 8) {
392       return AL_FORMAT_MONO8;
393     } else {
394       throw std::runtime_error("Only 16 and 8 bit samples supported");
395     }
396   }
397
398   throw std::runtime_error("Only 1 and 2 channel samples supported");
399 }
400
401 void
402 SoundManager::print_openal_version()
403 {
404   log_info << "OpenAL Vendor: " << alGetString(AL_VENDOR) << std::endl;
405   log_info << "OpenAL Version: " << alGetString(AL_VERSION) << std::endl;
406   log_info << "OpenAL Renderer: " << alGetString(AL_RENDERER) << std::endl;
407   log_info << "OpenAl Extensions: " << alGetString(AL_EXTENSIONS) << std::endl;
408 }
409
410 void
411 SoundManager::check_alc_error(const char* message)
412 {
413   int err = alcGetError(device);
414   if(err != ALC_NO_ERROR) {
415     std::stringstream msg;
416     msg << message << alcGetString(device, err);
417     throw std::runtime_error(msg.str());
418   }
419 }
420
421 void
422 SoundManager::check_al_error(const char* message)
423 {
424   int err = alGetError();
425   if(err != AL_NO_ERROR) {
426     std::stringstream msg;
427     msg << message << alGetString(err);
428     throw std::runtime_error(msg.str());
429   }
430 }