Fade out and pause music on death and resume on restart of level, fixes #1064
[supertux.git] / src / audio / sound_manager.cpp
1 //  SuperTux
2 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
3 //
4 //  This program is free software: you can redistribute it and/or modify
5 //  it under the terms of the GNU General Public License as published by
6 //  the Free Software Foundation, either version 3 of the License, or
7 //  (at your option) any later version.
8 //
9 //  This program is distributed in the hope that it will be useful,
10 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 //  GNU General Public License for more details.
13 //
14 //  You should have received a copy of the GNU General Public License
15 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17 #include "audio/sound_manager.hpp"
18
19 #include <SDL.h>
20 #include <assert.h>
21 #include <stdexcept>
22 #include <sstream>
23 #include <memory>
24
25 #include "audio/dummy_sound_source.hpp"
26 #include "audio/sound_file.hpp"
27 #include "audio/stream_sound_source.hpp"
28 #include "util/log.hpp"
29
30 SoundManager::SoundManager() :
31   device(0),
32   context(0),
33   sound_enabled(false),
34   buffers(),
35   sources(),
36   update_list(),
37   music_source(),
38   music_enabled(false),
39   current_music()
40 {
41   try {
42     device = alcOpenDevice(0);
43     if (device == NULL) {
44       throw std::runtime_error("Couldn't open audio device.");
45     }
46
47     int attributes[] = { 0 };
48     context = alcCreateContext(device, attributes);
49     check_alc_error("Couldn't create audio context: ");
50     alcMakeContextCurrent(context);
51     check_alc_error("Couldn't select audio context: ");
52
53     check_al_error("Audio error after init: ");
54     sound_enabled = true;
55     music_enabled = true;
56   } catch(std::exception& e) {
57     if(context != NULL) {
58       alcDestroyContext(context);
59       context = NULL;
60     }
61     if(device != NULL) {
62       alcCloseDevice(device);
63       device = NULL;
64     }
65     log_warning << "Couldn't initialize audio device: " << e.what() << std::endl;
66     print_openal_version();
67   }
68 }
69
70 SoundManager::~SoundManager()
71 {
72   music_source.reset();
73   sources.clear();
74
75   for(SoundBuffers::iterator i = buffers.begin(); i != buffers.end(); ++i) {
76     ALuint buffer = i->second;
77     alDeleteBuffers(1, &buffer);
78   }
79
80   if(context != NULL) {
81     alcDestroyContext(context);
82     context = NULL;
83   }
84   if(device != NULL) {
85     alcCloseDevice(device);
86     device = NULL;
87   }
88 }
89
90 ALuint
91 SoundManager::load_file_into_buffer(SoundFile& file)
92 {
93   ALenum format = get_sample_format(file);
94   ALuint buffer;
95   alGenBuffers(1, &buffer);
96   check_al_error("Couldn't create audio buffer: ");
97   std::unique_ptr<char[]> samples(new char[file.size]);
98   file.read(samples.get(), file.size);
99   alBufferData(buffer, format, samples.get(),
100                static_cast<ALsizei>(file.size),
101                static_cast<ALsizei>(file.rate));
102   check_al_error("Couldn't fill audio buffer: ");
103
104   return buffer;
105 }
106
107 std::unique_ptr<OpenALSoundSource>
108 SoundManager::intern_create_sound_source(const std::string& filename)
109 {
110   assert(sound_enabled);
111
112   std::unique_ptr<OpenALSoundSource> source(new OpenALSoundSource);
113
114   ALuint buffer;
115
116   // reuse an existing static sound buffer
117   SoundBuffers::iterator i = buffers.find(filename);
118   if(i != buffers.end()) {
119     buffer = i->second;
120   } else {
121     // Load sound file
122     std::unique_ptr<SoundFile> file(load_sound_file(filename));
123
124     if(file->size < 100000) {
125       buffer = load_file_into_buffer(*file);
126       buffers.insert(std::make_pair(filename, buffer));
127     } else {
128       std::unique_ptr<StreamSoundSource> source_(new StreamSoundSource);
129       source_->set_sound_file(std::move(file));
130       return std::move(source_);
131     }
132
133     log_debug << "Uncached sound \"" << filename << "\" requested to be played" << std::endl;
134   }
135
136   alSourcei(source->source, AL_BUFFER, buffer);
137   return std::move(source);
138 }
139
140 std::unique_ptr<SoundSource>
141 SoundManager::create_sound_source(const std::string& filename)
142 {
143   if(!sound_enabled)
144     return create_dummy_sound_source();
145
146   try {
147     return intern_create_sound_source(filename);
148   } catch(std::exception &e) {
149     log_warning << "Couldn't create audio source: " << e.what() << std::endl;
150     return create_dummy_sound_source();
151   }
152 }
153
154 void
155 SoundManager::preload(const std::string& filename)
156 {
157   if(!sound_enabled)
158     return;
159
160   SoundBuffers::iterator i = buffers.find(filename);
161   // already loaded?
162   if(i != buffers.end())
163     return;
164   try {
165     std::unique_ptr<SoundFile> file (load_sound_file(filename));
166     // only keep small files
167     if(file->size >= 100000)
168       return;
169
170     ALuint buffer = load_file_into_buffer(*file);
171     buffers.insert(std::make_pair(filename, buffer));
172   } catch(std::exception& e) {
173     log_warning << "Error while preloading sound file: " << e.what() << std::endl;
174   }
175 }
176
177 void
178 SoundManager::play(const std::string& filename, const Vector& pos)
179 {
180   if(!sound_enabled)
181     return;
182
183   try {
184     std::unique_ptr<OpenALSoundSource> source(intern_create_sound_source(filename));
185
186     if(pos.x < 0 || pos.y < 0) {
187       source->set_relative(true);
188     } else {
189       source->set_position(pos);
190     }
191     source->play();
192     sources.push_back(std::move(source));
193   } catch(std::exception& e) {
194     log_warning << "Couldn't play sound " << filename << ": " << e.what() << std::endl;
195   }
196 }
197
198 void
199 SoundManager::manage_source(std::unique_ptr<SoundSource> source)
200 {
201   assert(source);
202   if (dynamic_cast<OpenALSoundSource*>(source.get()))
203   {
204     std::unique_ptr<OpenALSoundSource> openal_source(dynamic_cast<OpenALSoundSource*>(source.release()));
205     sources.push_back(std::move(openal_source));
206   }
207 }
208
209 void
210 SoundManager::register_for_update(StreamSoundSource* sss)
211 {
212   if (sss)
213   {
214     update_list.push_back(sss);
215   }
216 }
217
218 void
219 SoundManager::remove_from_update(StreamSoundSource* sss)
220 {
221   if (sss)
222   {
223     StreamSoundSources::iterator i = update_list.begin();
224     while( i != update_list.end() ){
225       if( *i == sss ){
226         i = update_list.erase(i);
227       } else {
228         ++i;
229       }
230     }
231   }
232 }
233
234 void
235 SoundManager::enable_sound(bool enable)
236 {
237   if(device == NULL)
238     return;
239
240   sound_enabled = enable;
241 }
242
243 void
244 SoundManager::enable_music(bool enable)
245 {
246   if(device == NULL)
247     return;
248
249   music_enabled = enable;
250   if(music_enabled) {
251     play_music(current_music);
252   } else {
253     if(music_source) {
254       music_source.reset();
255     }
256   }
257 }
258
259 void
260 SoundManager::stop_music(float fadetime)
261 {
262   if(fadetime > 0) {
263     if(music_source
264        && music_source->get_fade_state() != StreamSoundSource::FadingOff)
265       music_source->set_fading(StreamSoundSource::FadingOff, fadetime);
266   } else {
267     music_source.reset();
268   }
269   current_music = "";
270 }
271
272 void
273 SoundManager::play_music(const std::string& filename, bool fade)
274 {
275   if(filename == current_music && music_source != NULL)
276     return;
277   current_music = filename;
278   if(!music_enabled)
279     return;
280
281   if(filename == "") {
282     music_source.reset();
283     return;
284   }
285
286   try {
287     std::unique_ptr<StreamSoundSource> newmusic (new StreamSoundSource());
288     newmusic->set_sound_file(load_sound_file(filename));
289     newmusic->set_looping(true);
290     newmusic->set_relative(true);
291     if(fade)
292       newmusic->set_fading(StreamSoundSource::FadingOn, .5f);
293     newmusic->play();
294
295     music_source = std::move(newmusic);
296   } catch(std::exception& e) {
297     log_warning << "Couldn't play music file '" << filename << "': " << e.what() << std::endl;
298     // When this happens, previous music continued playing, stop it, just in case.
299     stop_music(0);
300   }
301 }
302
303 void
304 SoundManager::pause_music(float fadetime)
305 {
306   if(fadetime > 0) {
307     if(music_source
308        && music_source->get_fade_state() != StreamSoundSource::FadingPause)
309       music_source->set_fading(StreamSoundSource::FadingPause, fadetime);
310   } else {
311     music_source->pause();
312   }
313 }
314
315 void
316 SoundManager::resume_music(float fadetime)
317 {
318   if(fadetime > 0) {
319     if(music_source
320        && music_source->get_fade_state() != StreamSoundSource::FadingResume)
321       music_source->set_fading(StreamSoundSource::FadingResume, fadetime);
322   } else {
323     music_source->resume();
324   }
325 }
326
327 void
328 SoundManager::set_listener_position(const Vector& pos)
329 {
330   static Uint32 lastticks = SDL_GetTicks();
331
332   Uint32 current_ticks = SDL_GetTicks();
333   if(current_ticks - lastticks < 300)
334     return;
335   lastticks = current_ticks;
336
337   alListener3f(AL_POSITION, pos.x, pos.y, 0);
338 }
339
340 void
341 SoundManager::set_listener_velocity(const Vector& vel)
342 {
343   alListener3f(AL_VELOCITY, vel.x, vel.y, 0);
344 }
345
346 void
347 SoundManager::update()
348 {
349   static Uint32 lasttime = SDL_GetTicks();
350   Uint32 now = SDL_GetTicks();
351
352   if(now - lasttime < 300)
353     return;
354   lasttime = now;
355
356   // update and check for finished sound sources
357   for(SoundSources::iterator i = sources.begin(); i != sources.end(); ) {
358     auto& source = *i;
359
360     source->update();
361
362     if(!source->playing()) {
363       i = sources.erase(i);
364     } else {
365       ++i;
366     }
367   }
368   // check streaming sounds
369   if(music_source) {
370     music_source->update();
371   }
372
373   if (context)
374   {
375     alcProcessContext(context);
376     check_alc_error("Error while processing audio context: ");
377   }
378
379   //run update() for stream_sound_source
380   StreamSoundSources::iterator s = update_list.begin();
381   while( s != update_list.end() ){
382     (*s)->update();
383     ++s;
384   }
385 }
386
387 ALenum
388 SoundManager::get_sample_format(const SoundFile& file)
389 {
390   if(file.channels == 2) {
391     if(file.bits_per_sample == 16) {
392       return AL_FORMAT_STEREO16;
393     } else if(file.bits_per_sample == 8) {
394       return AL_FORMAT_STEREO8;
395     } else {
396       throw std::runtime_error("Only 16 and 8 bit samples supported");
397     }
398   } else if(file.channels == 1) {
399     if(file.bits_per_sample == 16) {
400       return AL_FORMAT_MONO16;
401     } else if(file.bits_per_sample == 8) {
402       return AL_FORMAT_MONO8;
403     } else {
404       throw std::runtime_error("Only 16 and 8 bit samples supported");
405     }
406   }
407
408   throw std::runtime_error("Only 1 and 2 channel samples supported");
409 }
410
411 void
412 SoundManager::print_openal_version()
413 {
414   log_info << "OpenAL Vendor: " << alGetString(AL_VENDOR) << std::endl;
415   log_info << "OpenAL Version: " << alGetString(AL_VERSION) << std::endl;
416   log_info << "OpenAL Renderer: " << alGetString(AL_RENDERER) << std::endl;
417   log_info << "OpenAl Extensions: " << alGetString(AL_EXTENSIONS) << std::endl;
418 }
419
420 void
421 SoundManager::check_alc_error(const char* message)
422 {
423   int err = alcGetError(device);
424   if(err != ALC_NO_ERROR) {
425     std::stringstream msg;
426     msg << message << alcGetString(device, err);
427     throw std::runtime_error(msg.str());
428   }
429 }
430
431 void
432 SoundManager::check_al_error(const char* message)
433 {
434   int err = alGetError();
435   if(err != AL_NO_ERROR) {
436     std::stringstream msg;
437     msg << message << alGetString(err);
438     throw std::runtime_error(msg.str());
439   }
440 }
441
442 /* EOF */