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