Added a new SoundManager based on OpenAL. I also simplified the API along the
[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& soundname, const Vector& pos)
110 {
111   std::string filename = "sounds/";
112   filename += soundname;
113   filename += ".wav";
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)
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     newmusic->play();
169  
170     delete music_source;
171     music_source = newmusic;
172   } catch(std::exception& e) {
173     std::cerr << "Couldn't play music file '" << filename << "': "
174       << e.what() << "\n";
175   }
176 }
177
178 void
179 SoundManager::set_listener_position(Vector pos)
180 {
181   alListener3f(AL_POSITION, pos.x, pos.y, 0);
182 }
183
184 void
185 SoundManager::set_listener_velocity(Vector vel)
186 {
187   alListener3f(AL_VELOCITY, vel.x, vel.y, 0);
188 }
189
190 void
191 SoundManager::update()
192 {
193   // check for finished sound sources
194   for(SoundSources::iterator i = sources.begin(); i != sources.end(); ) {
195     SoundSource* source = *i;
196     if(!source->playing()) {
197       delete source;
198       i = sources.erase(i);
199     } else {
200       ++i;
201     }
202   }
203   // check streaming sounds
204   if(music_source)
205     music_source->update();
206   
207   alcProcessContext(context);
208   check_alc_error("Error while processing audio context: ");
209 }
210
211 ALenum
212 SoundManager::get_sample_format(SoundFile* file)
213 {
214   if(file->channels == 2) {
215     if(file->bits_per_sample == 16) {
216       return AL_FORMAT_STEREO16;
217     } else if(file->bits_per_sample == 8) {
218       return AL_FORMAT_STEREO8;
219     } else {
220       throw std::runtime_error("Only 16 and 8 bit samples supported");
221     }
222   } else if(file->channels == 1) {
223     if(file->bits_per_sample == 16) {
224       return AL_FORMAT_MONO16;
225     } else if(file->bits_per_sample == 8) {
226       return AL_FORMAT_MONO8;
227     } else {
228       throw std::runtime_error("Only 16 and 8 bit samples supported");
229     }
230   }
231   
232   throw std::runtime_error("Only 1 and 2 channel samples supported");
233 }
234
235 void
236 SoundManager::print_openal_version()
237 {
238   std::cout << "OpenAL Vendor: " << alGetString(AL_VENDOR) << "\n"
239             << "OpenAL Version: " << alGetString(AL_VERSION) << "\n" 
240             << "OpenAL Renderer: " << alGetString(AL_RENDERER) << "\n"
241             << "OpenAl Extensions: " << alGetString(AL_RENDERER) << "\n";
242 }
243
244 void
245 SoundManager::check_alc_error(const char* message)
246 {
247   int err = alcGetError(device);
248   if(err != ALC_NO_ERROR) {
249     std::stringstream msg;
250     msg << message << alcGetString(device, err);
251     throw std::runtime_error(msg.str());
252   }                
253 }
254
255 void
256 SoundManager::check_al_error(const char* message)
257 {
258   int err = alGetError();
259   if(err != AL_NO_ERROR) {
260     std::stringstream msg;
261     msg << message << alGetString(err);
262     throw std::runtime_error(msg.str());
263   }  
264 }
265