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