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