updated squirrel version
[supertux.git] / src / audio / sound_manager.cpp
1 #include "sound_manager.h"
2
3 #include <stdexcept>
4 #include <iostream>
5 #include <sstream>
6 #include <memory>
7
8 #include "sound_file.h"
9 #include "sound_source.h"
10 #include "stream_sound_source.h"
11
12 SoundManager::SoundManager()
13   : device(0), context(0), sound_enabled(false), music_source(0)
14 {
15   try {
16     device = alcOpenDevice(0);
17     if(device == 0) {
18       print_openal_version();
19       throw std::runtime_error("Couldn't open audio device.");
20     }
21
22     int attributes[] = { 0 };
23     context = alcCreateContext(device, attributes);
24     check_alc_error("Couldn't create audio context: ");
25     alcMakeContextCurrent(context);
26     check_alc_error("Couldn't select audio context: ");
27
28     check_al_error("Audio error after init: ");
29     sound_enabled = true;
30   } catch(std::exception& e) {
31     device = 0;
32     context = 0;
33     std::cerr << "Couldn't initialize audio device:" << e.what() << "\n";
34     print_openal_version();
35   }
36 }
37
38 SoundManager::~SoundManager()
39 {
40   delete music_source;
41
42   for(SoundSources::iterator i = sources.begin(); i != sources.end(); ++i) {
43     delete *i;
44   }
45
46   for(SoundBuffers::iterator i = buffers.begin(); i != buffers.end(); ++i) {
47     ALuint buffer = i->second;
48     alDeleteBuffers(1, &buffer);
49   }
50
51   if(context != 0) {
52     alcMakeContextCurrent(0);
53     alcDestroyContext(context);
54   }
55   if(device != 0) {
56     alcCloseDevice(device);
57   }
58 }
59
60 ALuint
61 SoundManager::load_file_into_buffer(const std::string& filename)
62 {
63   // open sound file
64   std::auto_ptr<SoundFile> file (load_sound_file(filename));
65   
66   ALenum format = get_sample_format(file.get());
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     buffer = load_file_into_buffer(filename);
100     buffers.insert(std::make_pair(filename, buffer));
101   }
102   
103   SoundSource* source = new SoundSource();
104   alSourcei(source->source, AL_BUFFER, buffer);
105   return source;  
106 }
107
108 void
109 SoundManager::play(const std::string& filename, const Vector& pos)
110 {
111   try {
112     SoundSource* source = create_sound_source(filename);
113     if(source == 0)
114       return;
115     if(pos == Vector(-1, -1)) {
116       alSourcef(source->source, AL_ROLLOFF_FACTOR, 0);
117     } else {
118       source->set_position(pos);
119     }
120     source->play();
121     sources.push_back(source);
122   } catch(std::exception& e) {
123     std::cout << "Couldn't play sound " << filename << ": " << e.what() << "\n";
124   }
125 }
126
127 void
128 SoundManager::enable_sound(bool enable)
129 {
130   if(device == 0)
131     return;
132   sound_enabled = enable;
133 }
134
135 void
136 SoundManager::enable_music(bool enable)
137 {
138   if(device == 0)
139     return;
140   music_enabled = enable;
141   if(music_enabled) {
142     play_music(current_music);
143   } else {
144     if(music_source) {
145       delete music_source;
146       music_source = 0;
147     }
148   }
149 }
150
151 void
152 SoundManager::play_music(const std::string& filename)
153 {
154   if(filename == current_music)
155     return;
156   current_music = filename;
157   if(!music_enabled)
158     return;
159
160   try {
161     StreamSoundSource* newmusic 
162       = new StreamSoundSource(load_sound_file(filename));
163
164     alSourcef(newmusic->source, AL_ROLLOFF_FACTOR, 0);
165     newmusic->play();
166  
167     delete music_source;
168     music_source = newmusic;
169   } catch(std::exception& e) {
170     std::cerr << "Couldn't play music file '" << filename << "': "
171       << e.what() << "\n";
172   }
173 }
174
175 void
176 SoundManager::set_listener_position(Vector pos)
177 {
178   alListener3f(AL_POSITION, pos.x, pos.y, 0);
179 }
180
181 void
182 SoundManager::set_listener_velocity(Vector vel)
183 {
184   alListener3f(AL_VELOCITY, vel.x, vel.y, 0);
185 }
186
187 void
188 SoundManager::update()
189 {
190   // check for finished sound sources
191   for(SoundSources::iterator i = sources.begin(); i != sources.end(); ) {
192     SoundSource* source = *i;
193     if(!source->playing()) {
194       delete source;
195       i = sources.erase(i);
196     } else {
197       ++i;
198     }
199   }
200   // check streaming sounds
201   if(music_source)
202     music_source->update();
203   
204   alcProcessContext(context);
205   check_alc_error("Error while processing audio context: ");
206 }
207
208 ALenum
209 SoundManager::get_sample_format(SoundFile* file)
210 {
211   if(file->channels == 2) {
212     if(file->bits_per_sample == 16) {
213       return AL_FORMAT_STEREO16;
214     } else if(file->bits_per_sample == 8) {
215       return AL_FORMAT_STEREO8;
216     } else {
217       throw std::runtime_error("Only 16 and 8 bit samples supported");
218     }
219   } else if(file->channels == 1) {
220     if(file->bits_per_sample == 16) {
221       return AL_FORMAT_MONO16;
222     } else if(file->bits_per_sample == 8) {
223       return AL_FORMAT_MONO8;
224     } else {
225       throw std::runtime_error("Only 16 and 8 bit samples supported");
226     }
227   }
228   
229   throw std::runtime_error("Only 1 and 2 channel samples supported");
230 }
231
232 void
233 SoundManager::print_openal_version()
234 {
235   std::cout << "OpenAL Vendor: " << alGetString(AL_VENDOR) << "\n"
236             << "OpenAL Version: " << alGetString(AL_VERSION) << "\n" 
237             << "OpenAL Renderer: " << alGetString(AL_RENDERER) << "\n"
238             << "OpenAl Extensions: " << alGetString(AL_RENDERER) << "\n";
239 }
240
241 void
242 SoundManager::check_alc_error(const char* message)
243 {
244   int err = alcGetError(device);
245   if(err != ALC_NO_ERROR) {
246     std::stringstream msg;
247     msg << message << alcGetString(device, err);
248     throw std::runtime_error(msg.str());
249   }                
250 }
251
252 void
253 SoundManager::check_al_error(const char* message)
254 {
255   int err = alGetError();
256   if(err != AL_NO_ERROR) {
257     std::stringstream msg;
258     msg << message << alGetString(err);
259     throw std::runtime_error(msg.str());
260   }  
261 }
262