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