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