cc10db7ce0265a42c25d58abedb0c710d8148f3b
[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::enable_sound(bool enable)
205 {
206   if(device == NULL)
207     return;
208
209   sound_enabled = enable;
210 }
211
212 void
213 SoundManager::enable_music(bool enable)
214 {
215   if(device == NULL)
216     return;
217
218   music_enabled = enable;
219   if(music_enabled) {
220     play_music(current_music);
221   } else {
222     if(music_source) {
223       delete music_source;
224       music_source = 0;
225     }
226   }
227 }
228
229 void
230 SoundManager::stop_music(float fadetime)
231 {
232   if(fadetime > 0) {
233     if(music_source
234         && music_source->get_fade_state() != StreamSoundSource::FadingOff)
235       music_source->set_fading(StreamSoundSource::FadingOff, fadetime);
236   } else {
237     delete music_source;
238     music_source = NULL;
239   }
240   current_music = "";
241 }
242
243 void
244 SoundManager::play_music(const std::string& filename, bool fade)
245 {
246   if(filename == current_music && music_source != NULL)
247     return;
248   current_music = filename;
249   if(!music_enabled)
250     return;
251
252   if(filename == "") {
253     delete music_source;
254     music_source = NULL;
255     return;
256   }
257
258   try {
259     std::auto_ptr<StreamSoundSource> newmusic (new StreamSoundSource());
260     alSourcef(newmusic->source, AL_ROLLOFF_FACTOR, 0);
261     newmusic->set_sound_file(load_sound_file(filename));
262     newmusic->set_looping(true);
263     if(fade)
264       newmusic->set_fading(StreamSoundSource::FadingOn, .5f);
265     newmusic->play();
266
267     delete music_source;
268     music_source = newmusic.release();
269   } catch(std::exception& e) {
270     log_warning << "Couldn't play music file '" << filename << "': " << e.what() << std::endl;
271   }
272 }
273
274 void
275 SoundManager::set_listener_position(const Vector& pos)
276 {
277   static Uint32 lastticks = 0;
278
279   Uint32 current_ticks = SDL_GetTicks();
280   if(current_ticks - lastticks < 300)
281     return;
282   lastticks = current_ticks;                 
283
284   alListener3f(AL_POSITION, pos.x, pos.y, 0);
285 }
286
287 void
288 SoundManager::set_listener_velocity(const Vector& vel)
289 {
290   alListener3f(AL_VELOCITY, vel.x, vel.y, 0);
291 }
292
293 void
294 SoundManager::update()
295 {
296   static float lasttime = real_time;
297
298   if(real_time - lasttime < 0.3)
299     return;
300   lasttime = real_time;
301
302   // update and check for finished sound sources
303   for(SoundSources::iterator i = sources.begin(); i != sources.end(); ) {
304     OpenALSoundSource* source = *i;
305
306     source->update();
307     
308     if(!source->playing()) {
309       delete source;
310       i = sources.erase(i);
311     } else {
312       ++i;
313     }
314   }
315   // check streaming sounds
316   if(music_source) {
317     music_source->update();
318   }
319
320   if (context)
321   {
322     alcProcessContext(context);
323     check_alc_error("Error while processing audio context: ");
324   }
325 }
326
327 ALenum
328 SoundManager::get_sample_format(SoundFile* file)
329 {
330   if(file->channels == 2) {
331     if(file->bits_per_sample == 16) {
332       return AL_FORMAT_STEREO16;
333     } else if(file->bits_per_sample == 8) {
334       return AL_FORMAT_STEREO8;
335     } else {
336       throw std::runtime_error("Only 16 and 8 bit samples supported");
337     }
338   } else if(file->channels == 1) {
339     if(file->bits_per_sample == 16) {
340       return AL_FORMAT_MONO16;
341     } else if(file->bits_per_sample == 8) {
342       return AL_FORMAT_MONO8;
343     } else {
344       throw std::runtime_error("Only 16 and 8 bit samples supported");
345     }
346   }
347   
348   throw std::runtime_error("Only 1 and 2 channel samples supported");
349 }
350
351 void
352 SoundManager::print_openal_version()
353 {
354   log_info << "OpenAL Vendor: " << alGetString(AL_VENDOR) << std::endl;
355   log_info << "OpenAL Version: " << alGetString(AL_VERSION) << std::endl;
356   log_info << "OpenAL Renderer: " << alGetString(AL_RENDERER) << std::endl;
357   log_info << "OpenAl Extensions: " << alGetString(AL_EXTENSIONS) << std::endl;
358 }
359
360 void
361 SoundManager::check_alc_error(const char* message)
362 {
363   int err = alcGetError(device);
364   if(err != ALC_NO_ERROR) {
365     std::stringstream msg;
366     msg << message << alcGetString(device, err);
367     throw std::runtime_error(msg.str());
368   }                
369 }
370
371 void
372 SoundManager::check_al_error(const char* message)
373 {
374   int err = alGetError();
375   if(err != AL_NO_ERROR) {
376     std::stringstream msg;
377     msg << message << alGetString(err);
378     throw std::runtime_error(msg.str());
379   }  
380 }
381