dc26cd83126000521fa0486e6f8efd2f22cf6fab
[supertux.git] / src / audio / sound_manager.cpp
1 #include "sound_manager.hpp"
2
3 #include <stdexcept>
4 #include <iostream>
5 #include <sstream>
6 #include <memory>
7
8 #include "sound_file.hpp"
9 #include "sound_source.hpp"
10 #include "stream_sound_source.hpp"
11 #include "msg.hpp"
12
13 SoundManager* sound_manager = 0;
14
15 SoundManager::SoundManager()
16   : device(0), context(0), sound_enabled(false), music_source(0),
17     music_enabled(true)
18 {
19   try {
20     device = alcOpenDevice(0);
21     if(device == 0) {
22       print_openal_version();
23       throw std::runtime_error("Couldn't open audio device.");
24     }
25
26     int attributes[] = { 0 };
27     context = alcCreateContext(device, attributes);
28     check_alc_error("Couldn't create audio context: ");
29     alcMakeContextCurrent(context);
30     check_alc_error("Couldn't select audio context: ");
31
32     check_al_error("Audio error after init: ");
33     sound_enabled = true;
34   } catch(std::exception& e) {
35     device = 0;
36     context = 0;
37     msg_warning("Couldn't initialize audio device:" << e.what());
38     print_openal_version();
39     throw e;
40   }
41 }
42
43 SoundManager::~SoundManager()
44 {
45   delete music_source;
46
47   for(SoundSources::iterator i = sources.begin(); i != sources.end(); ++i) {
48     delete *i;
49   }
50
51   for(SoundBuffers::iterator i = buffers.begin(); i != buffers.end(); ++i) {
52     ALuint buffer = i->second;
53     alDeleteBuffers(1, &buffer);
54   }
55
56   if(context != 0) {
57     alcDestroyContext(context);
58   }
59   if(device != 0) {
60     alcCloseDevice(device);
61   }
62 }
63
64 ALuint
65 SoundManager::load_file_into_buffer(SoundFile* file)
66 {
67   ALenum format = get_sample_format(file);
68   ALuint buffer;
69   alGenBuffers(1, &buffer);
70   check_al_error("Couldn't create audio buffer: ");
71   char* samples = new char[file->size];
72   try {
73     file->read(samples, file->size);
74     alBufferData(buffer, format, samples,
75         static_cast<ALsizei> (file->size),
76         static_cast<ALsizei> (file->rate));
77     check_al_error("Couldn't fill audio buffer: ");
78   } catch(...) {
79     delete[] samples;
80     throw;
81   }
82   delete[] samples;
83
84   return buffer;
85 }
86
87 SoundSource*
88 SoundManager::create_sound_source(const std::string& filename)
89 {
90   if(!sound_enabled)
91     return 0;
92
93   ALuint buffer;
94   
95   // reuse an existing static sound buffer            
96   SoundBuffers::iterator i = buffers.find(filename);
97   if(i != buffers.end()) {
98     buffer = i->second;
99   } else {
100     // Load sound file
101     std::auto_ptr<SoundFile> file (load_sound_file(filename));
102
103     if(file->size < 100000) {
104       buffer = load_file_into_buffer(file.get());
105       buffers.insert(std::make_pair(filename, buffer));
106     } else {
107       StreamSoundSource* source = new StreamSoundSource();
108       source->set_sound_file(file.release());
109       return source;
110     }
111   }
112   
113   SoundSource* source = new SoundSource();
114   alSourcei(source->source, AL_BUFFER, buffer);
115   return source;  
116 }
117
118 void
119 SoundManager::play(const std::string& filename, const Vector& pos)
120 {
121   try {
122     SoundSource* source = create_sound_source(filename);
123     if(source == 0)
124       return;
125     if(pos == Vector(-1, -1)) {
126       alSourcef(source->source, AL_ROLLOFF_FACTOR, 0);
127     } else {
128       source->set_position(pos);
129     }
130     source->play();
131     sources.push_back(source);
132   } catch(std::exception& e) {
133     msg_warning("Couldn't play sound " << filename << ": " << e.what());
134   }
135 }
136
137 void
138 SoundManager::enable_sound(bool enable)
139 {
140   if(device == 0)
141     return;
142   sound_enabled = enable;
143 }
144
145 void
146 SoundManager::enable_music(bool enable)
147 {
148   if(device == 0)
149     return;
150   music_enabled = enable;
151   if(music_enabled) {
152     play_music(current_music);
153   } else {
154     if(music_source) {
155       delete music_source;
156       music_source = 0;
157     }
158   }
159 }
160
161 void
162 SoundManager::stop_music(float fadetime)
163 {
164   if(fadetime > 0) {
165     if(music_source
166         && music_source->get_fade_state() != StreamSoundSource::FadingOff)
167       music_source->set_fading(StreamSoundSource::FadingOff, fadetime);
168   } else {
169     delete music_source;
170     music_source = 0;
171   }
172   current_music = "";
173 }
174
175 void
176 SoundManager::play_music(const std::string& filename, bool fade)
177 {
178   if(filename == current_music && music_source != NULL)
179     return;
180   current_music = filename;
181   if(!music_enabled)
182     return;
183
184   if(filename == "") {
185     delete music_source;
186     music_source = 0;
187     return;
188   }
189
190   try {
191     std::auto_ptr<StreamSoundSource> newmusic (new StreamSoundSource());
192     alSourcef(newmusic->source, AL_ROLLOFF_FACTOR, 0);
193     newmusic->set_sound_file(load_sound_file(filename));
194     newmusic->set_looping(true);
195     if(fade)
196       newmusic->set_fading(StreamSoundSource::FadingOn, .5f);
197     newmusic->play();
198
199     delete music_source;
200     music_source = newmusic.release();
201   } catch(std::exception& e) {
202     msg_warning("Couldn't play music file '" << filename << "': "
203       << e.what());
204   }
205 }
206
207 void
208 SoundManager::set_listener_position(const Vector& pos)
209 {
210   static Uint32 lastticks = 0;
211
212   Uint32 current_ticks = SDL_GetTicks();
213   if(current_ticks - lastticks < 300)
214     return;
215   lastticks = current_ticks;                 
216
217   alListener3f(AL_POSITION, pos.x, pos.y, 0);
218 }
219
220 void
221 SoundManager::set_listener_velocity(const Vector& vel)
222 {
223   alListener3f(AL_VELOCITY, vel.x, vel.y, 0);
224 }
225
226 void
227 SoundManager::update()
228 {
229   static Uint32 lastticks = 0;
230
231   Uint32 current_ticks = SDL_GetTicks();
232   if(current_ticks - lastticks < 300)
233     return;
234   lastticks = current_ticks;
235
236   // update and check for finished sound sources
237   for(SoundSources::iterator i = sources.begin(); i != sources.end(); ) {
238     SoundSource* source = *i;
239
240     source->update();
241     
242     if(!source->playing()) {
243       delete source;
244       i = sources.erase(i);
245     } else {
246       ++i;
247     }
248   }
249   // check streaming sounds
250   if(music_source) {
251     music_source->update();
252   }
253   
254   alcProcessContext(context);
255   check_alc_error("Error while processing audio context: ");
256 }
257
258 ALenum
259 SoundManager::get_sample_format(SoundFile* file)
260 {
261   if(file->channels == 2) {
262     if(file->bits_per_sample == 16) {
263       return AL_FORMAT_STEREO16;
264     } else if(file->bits_per_sample == 8) {
265       return AL_FORMAT_STEREO8;
266     } else {
267       throw std::runtime_error("Only 16 and 8 bit samples supported");
268     }
269   } else if(file->channels == 1) {
270     if(file->bits_per_sample == 16) {
271       return AL_FORMAT_MONO16;
272     } else if(file->bits_per_sample == 8) {
273       return AL_FORMAT_MONO8;
274     } else {
275       throw std::runtime_error("Only 16 and 8 bit samples supported");
276     }
277   }
278   
279   throw std::runtime_error("Only 1 and 2 channel samples supported");
280 }
281
282 void
283 SoundManager::print_openal_version()
284 {
285   msg_info("OpenAL Vendor: " << alGetString(AL_VENDOR));
286   msg_info("OpenAL Version: " << alGetString(AL_VERSION));
287   msg_info("OpenAL Renderer: " << alGetString(AL_RENDERER));
288   msg_info("OpenAl Extensions: " << alGetString(AL_EXTENSIONS));
289 }
290
291 void
292 SoundManager::check_alc_error(const char* message)
293 {
294   int err = alcGetError(device);
295   if(err != ALC_NO_ERROR) {
296     std::stringstream msg;
297     msg << message << alcGetString(device, err);
298     throw std::runtime_error(msg.str());
299   }                
300 }
301
302 void
303 SoundManager::check_al_error(const char* message)
304 {
305   int err = alGetError();
306   if(err != AL_NO_ERROR) {
307     std::stringstream msg;
308     msg << message << alGetString(err);
309     throw std::runtime_error(msg.str());
310   }  
311 }
312