- sounds are on both channels
[supertux.git] / src / audio / sound_manager.cpp
1 //  SuperTux
2 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
3 //
4 //  This program is free software: you can redistribute it and/or modify
5 //  it under the terms of the GNU General Public License as published by
6 //  the Free Software Foundation, either version 3 of the License, or
7 //  (at your option) any later version.
8 //
9 //  This program is distributed in the hope that it will be useful,
10 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 //  GNU General Public License for more details.
13 //
14 //  You should have received a copy of the GNU General Public License
15 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17 #include "audio/sound_manager.hpp"
18
19 #include <SDL.h>
20 #include <assert.h>
21 #include <stdexcept>
22 #include <sstream>
23 #include <memory>
24
25 #include "audio/dummy_sound_source.hpp"
26 #include "audio/sound_file.hpp"
27 #include "audio/stream_sound_source.hpp"
28 #include "util/log.hpp"
29
30 #ifdef NDEBUG
31 /** Older openal versions often miss this function and it isn't that vital for
32  * supertux...
33  */
34 #ifdef alcGetString
35 #  undef alcGetString
36 #endif
37 #  define alcGetString(x,y) ""
38 #endif
39
40 SoundManager::SoundManager() :
41   device(0), 
42   context(0), 
43   sound_enabled(false), 
44   buffers(),
45   sources(),
46   update_list(),
47   music_source(0),
48   music_enabled(false),
49   current_music()
50 {
51   try {
52     device = alcOpenDevice(0);
53     if (device == NULL) {
54       throw std::runtime_error("Couldn't open audio device.");
55     }
56
57     int attributes[] = { 0 };
58     context = alcCreateContext(device, attributes);
59     check_alc_error("Couldn't create audio context: ");
60     alcMakeContextCurrent(context);
61     check_alc_error("Couldn't select audio context: ");
62
63     check_al_error("Audio error after init: ");
64     sound_enabled = true;
65     music_enabled = true;
66   } catch(std::exception& e) {
67     if(context != NULL)
68       alcDestroyContext(context);
69     context = NULL;
70     if(device != NULL)
71       alcCloseDevice(device);
72     device = NULL;
73     log_warning << "Couldn't initialize audio device: " << e.what() << std::endl;
74     print_openal_version();
75   }
76 }
77
78 SoundManager::~SoundManager()
79 {
80   delete music_source;
81
82   for(SoundSources::iterator i = sources.begin(); i != sources.end(); ++i) {
83     delete *i;
84   }
85
86   for(SoundBuffers::iterator i = buffers.begin(); i != buffers.end(); ++i) {
87     ALuint buffer = i->second;
88     alDeleteBuffers(1, &buffer);
89   }
90
91   if(context != NULL) {
92     alcDestroyContext(context);
93   }
94   if(device != NULL) {
95     alcCloseDevice(device);
96   }
97 }
98
99 ALuint
100 SoundManager::load_file_into_buffer(SoundFile* file)
101 {
102   ALenum format = get_sample_format(file);
103   ALuint buffer;
104   alGenBuffers(1, &buffer);
105   check_al_error("Couldn't create audio buffer: ");
106   char* samples = new char[file->size];
107   try {
108     file->read(samples, file->size);
109     alBufferData(buffer, format, samples,
110                  static_cast<ALsizei> (file->size),
111                  static_cast<ALsizei> (file->rate));
112     check_al_error("Couldn't fill audio buffer: ");
113   } catch(...) {
114     delete[] samples;
115     throw;
116   }
117   delete[] samples;
118
119   return buffer;
120 }
121
122 OpenALSoundSource*
123 SoundManager::intern_create_sound_source(const std::string& filename)
124 {
125   assert(sound_enabled);
126
127   std::auto_ptr<OpenALSoundSource> source (new OpenALSoundSource());
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     // Load sound file
137     std::auto_ptr<SoundFile> file (load_sound_file(filename));
138
139     if(file->size < 100000) {
140       buffer = load_file_into_buffer(file.get());
141       buffers.insert(std::make_pair(filename, buffer));
142     } else {
143       StreamSoundSource* source = new StreamSoundSource();
144       source->set_sound_file(file.release());
145       return source;
146     }
147
148     log_debug << "Uncached sound \"" << filename << "\" requested to be played" << std::endl;
149   }
150
151   alSourcei(source->source, AL_BUFFER, buffer);
152   return source.release();
153 }
154
155 SoundSource*
156 SoundManager::create_sound_source(const std::string& filename)
157 {
158   if(!sound_enabled)
159     return create_dummy_sound_source();
160
161   try {
162     return intern_create_sound_source(filename);
163   } catch(std::exception &e) {
164     log_warning << "Couldn't create audio source: " << e.what() << std::endl;
165     return create_dummy_sound_source();
166   }
167 }
168
169 void
170 SoundManager::preload(const std::string& filename)
171 {
172   if(!sound_enabled)
173     return;
174
175   SoundBuffers::iterator i = buffers.find(filename);
176   // already loaded?
177   if(i != buffers.end())
178     return;
179   try {
180     std::auto_ptr<SoundFile> file (load_sound_file(filename));
181     // only keep small files
182     if(file->size >= 100000)
183       return;
184
185     ALuint buffer = load_file_into_buffer(file.get());
186     buffers.insert(std::make_pair(filename, buffer));
187   } catch(std::exception& e) {
188     log_warning << "Error while preloading sound file: " << e.what() << std::endl;
189   }
190 }
191
192 void
193 SoundManager::play(const std::string& filename, const Vector& pos)
194 {
195   if(!sound_enabled)
196     return;
197
198   try {
199     std::auto_ptr<OpenALSoundSource> source
200       (intern_create_sound_source(filename));
201
202     if(pos.x < 0 || pos.y < 0) {
203       source->set_relative(true);
204     } else {
205       source->set_position(pos);
206     }
207     source->play();
208     sources.push_back(source.release());
209   } catch(std::exception& e) {
210     log_warning << "Couldn't play sound " << filename << ": " << e.what() << std::endl;
211   }
212 }
213
214 void
215 SoundManager::manage_source(SoundSource* source)
216 {
217   assert(source != NULL);
218
219   OpenALSoundSource* openal_source = dynamic_cast<OpenALSoundSource*> (source);
220   if(openal_source != NULL) {
221     sources.push_back(openal_source);
222   }
223 }
224
225 void
226 SoundManager::register_for_update( StreamSoundSource* sss ){
227   if( sss != NULL ){
228     update_list.push_back( sss );
229   }
230 }
231
232 void
233 SoundManager::remove_from_update( StreamSoundSource* sss  ){
234   if( sss != NULL ){
235     StreamSoundSources::iterator i = update_list.begin();
236     while( i != update_list.end() ){
237       if( *i == sss ){
238         i = update_list.erase(i);
239       } else {
240         i++;
241       }
242     }
243   }
244 }
245
246 void
247 SoundManager::enable_sound(bool enable)
248 {
249   if(device == NULL)
250     return;
251
252   sound_enabled = enable;
253 }
254
255 void
256 SoundManager::enable_music(bool enable)
257 {
258   if(device == NULL)
259     return;
260
261   music_enabled = enable;
262   if(music_enabled) {
263     play_music(current_music);
264   } else {
265     if(music_source) {
266       delete music_source;
267       music_source = 0;
268     }
269   }
270 }
271
272 void
273 SoundManager::stop_music(float fadetime)
274 {
275   if(fadetime > 0) {
276     if(music_source
277        && music_source->get_fade_state() != StreamSoundSource::FadingOff)
278       music_source->set_fading(StreamSoundSource::FadingOff, fadetime);
279   } else {
280     delete music_source;
281     music_source = NULL;
282   }
283   current_music = "";
284 }
285
286 void
287 SoundManager::play_music(const std::string& filename, bool fade)
288 {
289   if(filename == current_music && music_source != NULL)
290     return;
291   current_music = filename;
292   if(!music_enabled)
293     return;
294
295   if(filename == "") {
296     delete music_source;
297     music_source = NULL;
298     return;
299   }
300
301   try {
302     std::auto_ptr<StreamSoundSource> newmusic (new StreamSoundSource());
303     newmusic->set_sound_file(load_sound_file(filename));
304     newmusic->set_looping(true);
305     newmusic->set_relative(true);
306     if(fade)
307       newmusic->set_fading(StreamSoundSource::FadingOn, .5f);
308     newmusic->play();
309
310     delete music_source;
311     music_source = newmusic.release();
312   } catch(std::exception& e) {
313     log_warning << "Couldn't play music file '" << filename << "': " << e.what() << std::endl;
314   }
315 }
316
317 void
318 SoundManager::set_listener_position(const Vector& pos)
319 {
320   static Uint32 lastticks = SDL_GetTicks();
321
322   Uint32 current_ticks = SDL_GetTicks();
323   if(current_ticks - lastticks < 300)
324     return;
325   lastticks = current_ticks;
326
327   alListener3f(AL_POSITION, pos.x, pos.y, 0);
328 }
329
330 void
331 SoundManager::set_listener_velocity(const Vector& vel)
332 {
333   alListener3f(AL_VELOCITY, vel.x, vel.y, 0);
334 }
335
336 void
337 SoundManager::update()
338 {
339   static Uint32 lasttime = SDL_GetTicks();
340   Uint32 now = SDL_GetTicks();
341
342   if(now - lasttime < 300)
343     return;
344   lasttime = now;
345
346   // update and check for finished sound sources
347   for(SoundSources::iterator i = sources.begin(); i != sources.end(); ) {
348     OpenALSoundSource* source = *i;
349
350     source->update();
351
352     if(!source->playing()) {
353       delete source;
354       i = sources.erase(i);
355     } else {
356       ++i;
357     }
358   }
359   // check streaming sounds
360   if(music_source) {
361     music_source->update();
362   }
363
364   if (context)
365   {
366     alcProcessContext(context);
367     check_alc_error("Error while processing audio context: ");
368   }
369
370   //run update() for stream_sound_source
371   StreamSoundSources::iterator s = update_list.begin();
372   while( s != update_list.end() ){
373     (*s)->update();
374     s++;
375   }
376 }
377
378 ALenum
379 SoundManager::get_sample_format(SoundFile* file)
380 {
381   if(file->channels == 2) {
382     if(file->bits_per_sample == 16) {
383       return AL_FORMAT_STEREO16;
384     } else if(file->bits_per_sample == 8) {
385       return AL_FORMAT_STEREO8;
386     } else {
387       throw std::runtime_error("Only 16 and 8 bit samples supported");
388     }
389   } else if(file->channels == 1) {
390     if(file->bits_per_sample == 16) {
391       return AL_FORMAT_MONO16;
392     } else if(file->bits_per_sample == 8) {
393       return AL_FORMAT_MONO8;
394     } else {
395       throw std::runtime_error("Only 16 and 8 bit samples supported");
396     }
397   }
398
399   throw std::runtime_error("Only 1 and 2 channel samples supported");
400 }
401
402 void
403 SoundManager::print_openal_version()
404 {
405   log_info << "OpenAL Vendor: " << alGetString(AL_VENDOR) << std::endl;
406   log_info << "OpenAL Version: " << alGetString(AL_VERSION) << std::endl;
407   log_info << "OpenAL Renderer: " << alGetString(AL_RENDERER) << std::endl;
408   log_info << "OpenAl Extensions: " << alGetString(AL_EXTENSIONS) << std::endl;
409 }
410
411 void
412 SoundManager::check_alc_error(const char* message)
413 {
414   int err = alcGetError(device);
415   if(err != ALC_NO_ERROR) {
416     std::stringstream msg;
417     msg << message << alcGetString(device, err);
418     throw std::runtime_error(msg.str());
419   }
420 }
421
422 void
423 SoundManager::check_al_error(const char* message)
424 {
425   int err = alGetError();
426   if(err != AL_NO_ERROR) {
427     std::stringstream msg;
428     msg << message << alGetString(err);
429     throw std::runtime_error(msg.str());
430   }
431 }
432
433 /* EOF */