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