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