Fix music not properly fading in again
[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(music_source == NULL)
307     return;
308
309   if(fadetime > 0) {
310     if(music_source
311        && music_source->get_fade_state() != StreamSoundSource::FadingPause)
312       music_source->set_fading(StreamSoundSource::FadingPause, fadetime);
313   } else {
314     music_source->pause();
315   }
316 }
317
318 void
319 SoundManager::resume_music(float fadetime)
320 {
321   if(music_source == NULL)
322     return;
323
324   if(fadetime > 0) {
325     if(music_source
326        && music_source->get_fade_state() != StreamSoundSource::FadingResume)
327       music_source->set_fading(StreamSoundSource::FadingResume, fadetime);
328   } else {
329     music_source->resume();
330   }
331 }
332
333 void
334 SoundManager::set_listener_position(const Vector& pos)
335 {
336   static Uint32 lastticks = SDL_GetTicks();
337
338   Uint32 current_ticks = SDL_GetTicks();
339   if(current_ticks - lastticks < 300)
340     return;
341   lastticks = current_ticks;
342
343   alListener3f(AL_POSITION, pos.x, pos.y, 0);
344 }
345
346 void
347 SoundManager::set_listener_velocity(const Vector& vel)
348 {
349   alListener3f(AL_VELOCITY, vel.x, vel.y, 0);
350 }
351
352 void
353 SoundManager::update()
354 {
355   static Uint32 lasttime = SDL_GetTicks();
356   Uint32 now = SDL_GetTicks();
357
358   if(now - lasttime < 300)
359     return;
360   lasttime = now;
361
362   // update and check for finished sound sources
363   for(SoundSources::iterator i = sources.begin(); i != sources.end(); ) {
364     auto& source = *i;
365
366     source->update();
367
368     if(!source->playing()) {
369       i = sources.erase(i);
370     } else {
371       ++i;
372     }
373   }
374   // check streaming sounds
375   if(music_source) {
376     music_source->update();
377   }
378
379   if (context)
380   {
381     alcProcessContext(context);
382     check_alc_error("Error while processing audio context: ");
383   }
384
385   //run update() for stream_sound_source
386   StreamSoundSources::iterator s = update_list.begin();
387   while( s != update_list.end() ){
388     (*s)->update();
389     ++s;
390   }
391 }
392
393 ALenum
394 SoundManager::get_sample_format(const SoundFile& file)
395 {
396   if(file.channels == 2) {
397     if(file.bits_per_sample == 16) {
398       return AL_FORMAT_STEREO16;
399     } else if(file.bits_per_sample == 8) {
400       return AL_FORMAT_STEREO8;
401     } else {
402       throw std::runtime_error("Only 16 and 8 bit samples supported");
403     }
404   } else if(file.channels == 1) {
405     if(file.bits_per_sample == 16) {
406       return AL_FORMAT_MONO16;
407     } else if(file.bits_per_sample == 8) {
408       return AL_FORMAT_MONO8;
409     } else {
410       throw std::runtime_error("Only 16 and 8 bit samples supported");
411     }
412   }
413
414   throw std::runtime_error("Only 1 and 2 channel samples supported");
415 }
416
417 void
418 SoundManager::print_openal_version()
419 {
420   log_info << "OpenAL Vendor: " << alGetString(AL_VENDOR) << std::endl;
421   log_info << "OpenAL Version: " << alGetString(AL_VERSION) << std::endl;
422   log_info << "OpenAL Renderer: " << alGetString(AL_RENDERER) << std::endl;
423   log_info << "OpenAl Extensions: " << alGetString(AL_EXTENSIONS) << std::endl;
424 }
425
426 void
427 SoundManager::check_alc_error(const char* message)
428 {
429   int err = alcGetError(device);
430   if(err != ALC_NO_ERROR) {
431     std::stringstream msg;
432     msg << message << alcGetString(device, err);
433     throw std::runtime_error(msg.str());
434   }
435 }
436
437 void
438 SoundManager::check_al_error(const char* message)
439 {
440   int err = alGetError();
441   if(err != AL_NO_ERROR) {
442     std::stringstream msg;
443     msg << message << alGetString(err);
444     throw std::runtime_error(msg.str());
445   }
446 }
447
448 /* EOF */