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