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