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