Massive copyright update. I'm sorry if I'm crediting Matze for something he didn...
[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
32 SoundManager* sound_manager = 0;
33
34 SoundManager::SoundManager()
35   : device(0), context(0), sound_enabled(false), music_source(0),
36     music_enabled(true)
37 {
38   try {
39     device = alcOpenDevice(0);
40     if(device == 0) {
41       print_openal_version();
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     device = 0;
55     context = 0;
56     log_warning << "Couldn't initialize audio device:" << e.what() << std::endl;
57     print_openal_version();
58     throw e;
59   }
60 }
61
62 SoundManager::~SoundManager()
63 {
64   delete music_source;
65
66   for(SoundSources::iterator i = sources.begin(); i != sources.end(); ++i) {
67     delete *i;
68   }
69
70   for(SoundBuffers::iterator i = buffers.begin(); i != buffers.end(); ++i) {
71     ALuint buffer = i->second;
72     alDeleteBuffers(1, &buffer);
73   }
74
75   if(context != 0) {
76     alcDestroyContext(context);
77   }
78   if(device != 0) {
79     alcCloseDevice(device);
80   }
81 }
82
83 ALuint
84 SoundManager::load_file_into_buffer(SoundFile* file)
85 {
86   ALenum format = get_sample_format(file);
87   ALuint buffer;
88   alGenBuffers(1, &buffer);
89   check_al_error("Couldn't create audio buffer: ");
90   char* samples = new char[file->size];
91   try {
92     file->read(samples, file->size);
93     alBufferData(buffer, format, samples,
94         static_cast<ALsizei> (file->size),
95         static_cast<ALsizei> (file->rate));
96     check_al_error("Couldn't fill audio buffer: ");
97   } catch(...) {
98     delete[] samples;
99     throw;
100   }
101   delete[] samples;
102
103   return buffer;
104 }
105
106 SoundSource*
107 SoundManager::create_sound_source(const std::string& filename)
108 {
109   if(!sound_enabled)
110     return 0;
111
112   ALuint buffer;
113   
114   // reuse an existing static sound buffer            
115   SoundBuffers::iterator i = buffers.find(filename);
116   if(i != buffers.end()) {
117     buffer = i->second;
118   } else {
119     // Load sound file
120     std::auto_ptr<SoundFile> file (load_sound_file(filename));
121
122     if(file->size < 100000) {
123       buffer = load_file_into_buffer(file.get());
124       buffers.insert(std::make_pair(filename, buffer));
125     } else {
126       StreamSoundSource* source = new StreamSoundSource();
127       source->set_sound_file(file.release());
128       return source;
129     }
130   }
131   
132   SoundSource* source = new SoundSource();
133   alSourcei(source->source, AL_BUFFER, buffer);
134   return source;  
135 }
136
137 void
138 SoundManager::play(const std::string& filename, const Vector& pos)
139 {
140   try {
141     SoundSource* source = create_sound_source(filename);
142     if(source == 0)
143       return;
144     if(pos == Vector(-1, -1)) {
145       alSourcef(source->source, AL_ROLLOFF_FACTOR, 0);
146     } else {
147       source->set_position(pos);
148     }
149     source->play();
150     sources.push_back(source);
151   } catch(std::exception& e) {
152     log_warning << "Couldn't play sound " << filename << ": " << e.what() << std::endl;
153   }
154 }
155
156 void
157 SoundManager::enable_sound(bool enable)
158 {
159   if(device == 0)
160     return;
161   sound_enabled = enable;
162 }
163
164 void
165 SoundManager::enable_music(bool enable)
166 {
167   if(device == 0)
168     return;
169   music_enabled = enable;
170   if(music_enabled) {
171     play_music(current_music);
172   } else {
173     if(music_source) {
174       delete music_source;
175       music_source = 0;
176     }
177   }
178 }
179
180 void
181 SoundManager::stop_music(float fadetime)
182 {
183   if(fadetime > 0) {
184     if(music_source
185         && music_source->get_fade_state() != StreamSoundSource::FadingOff)
186       music_source->set_fading(StreamSoundSource::FadingOff, fadetime);
187   } else {
188     delete music_source;
189     music_source = 0;
190   }
191   current_music = "";
192 }
193
194 void
195 SoundManager::play_music(const std::string& filename, bool fade)
196 {
197   if(filename == current_music && music_source != NULL)
198     return;
199   current_music = filename;
200   if(!music_enabled)
201     return;
202
203   if(filename == "") {
204     delete music_source;
205     music_source = 0;
206     return;
207   }
208
209   try {
210     std::auto_ptr<StreamSoundSource> newmusic (new StreamSoundSource());
211     alSourcef(newmusic->source, AL_ROLLOFF_FACTOR, 0);
212     newmusic->set_sound_file(load_sound_file(filename));
213     newmusic->set_looping(true);
214     if(fade)
215       newmusic->set_fading(StreamSoundSource::FadingOn, .5f);
216     newmusic->play();
217
218     delete music_source;
219     music_source = newmusic.release();
220   } catch(std::exception& e) {
221     log_warning << "Couldn't play music file '" << filename << "': " << e.what() << std::endl;
222   }
223 }
224
225 void
226 SoundManager::set_listener_position(const Vector& pos)
227 {
228   static Uint32 lastticks = 0;
229
230   Uint32 current_ticks = SDL_GetTicks();
231   if(current_ticks - lastticks < 300)
232     return;
233   lastticks = current_ticks;                 
234
235   alListener3f(AL_POSITION, pos.x, pos.y, 0);
236 }
237
238 void
239 SoundManager::set_listener_velocity(const Vector& vel)
240 {
241   alListener3f(AL_VELOCITY, vel.x, vel.y, 0);
242 }
243
244 void
245 SoundManager::update()
246 {
247   static Uint32 lastticks = 0;
248
249   Uint32 current_ticks = SDL_GetTicks();
250   if(current_ticks - lastticks < 300)
251     return;
252   lastticks = current_ticks;
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