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