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