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