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