f7bce6546d73d9ba1a371ec6684bf94b5084ed0f
[oweals/minetest.git] / src / sound_openal.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2012 celeron55, Perttu Ahola <celeron55@gmail.com>
4 OpenAL support based on work by:
5 Copyright (C) 2011 Sebastian 'Bahamada' Rühl
6 Copyright (C) 2011 Cyriaque 'Cisoun' Skrapits <cysoun@gmail.com>
7 Copyright (C) 2011 Giuseppe Bilotta <giuseppe.bilotta@gmail.com>
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License along
20 with this program; ifnot, write to the Free Software Foundation, Inc.,
21 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 */
23
24 #include "sound_openal.h"
25
26 #if defined(_WIN32)
27         #include <al.h>
28         #include <alc.h>
29         //#include <alext.h>
30 #elif defined(__APPLE__)
31         #include <OpenAL/al.h>
32         #include <OpenAL/alc.h>
33         #include <OpenAL/alext.h>
34 #else
35         #include <AL/al.h>
36         #include <AL/alc.h>
37         #include <AL/alext.h>
38 #endif
39 #include <vorbis/vorbisfile.h>
40 #include "log.h"
41 #include <map>
42 #include <vector>
43 #include "utility.h" // myrand()
44
45 #define BUFFER_SIZE 30000
46
47 static const char *alcErrorString(ALCenum err)
48 {
49         switch (err) {
50         case ALC_NO_ERROR:
51                 return "no error";
52         case ALC_INVALID_DEVICE:
53                 return "invalid device";
54         case ALC_INVALID_CONTEXT:
55                 return "invalid context";
56         case ALC_INVALID_ENUM:
57                 return "invalid enum";
58         case ALC_INVALID_VALUE:
59                 return "invalid value";
60         case ALC_OUT_OF_MEMORY:
61                 return "out of memory";
62         default:
63                 return "<unknown OpenAL error>";
64         }
65 }
66
67 static const char *alErrorString(ALenum err)
68 {
69         switch (err) {
70         case AL_NO_ERROR:
71                 return "no error";
72         case AL_INVALID_NAME:
73                 return "invalid name";
74         case AL_INVALID_ENUM:
75                 return "invalid enum";
76         case AL_INVALID_VALUE:
77                 return "invalid value";
78         case AL_INVALID_OPERATION:
79                 return "invalid operation";
80         case AL_OUT_OF_MEMORY:
81                 return "out of memory";
82         default:
83                 return "<unknown OpenAL error>";
84         }
85 }
86
87 static ALenum warn_if_error(ALenum err, const char *desc)
88 {
89         if(err == AL_NO_ERROR)
90                 return err;
91         errorstream<<"WARNING: "<<desc<<": "<<alErrorString(err)<<std::endl;
92         return err;
93 }
94
95 void f3_set(ALfloat *f3, v3f v)
96 {
97         f3[0] = v.X;
98         f3[1] = v.Y;
99         f3[2] = v.Z;
100 }
101
102 struct SoundBuffer
103 {
104         ALenum format;
105         ALsizei freq;
106         ALuint buffer_id;
107         std::vector<char> buffer;
108 };
109
110 SoundBuffer* loadOggFile(const std::string &filepath)
111 {
112         int endian = 0; // 0 for Little-Endian, 1 for Big-Endian
113         int bitStream;
114         long bytes;
115         char array[BUFFER_SIZE]; // Local fixed size array
116         vorbis_info *pInfo;
117         OggVorbis_File oggFile;
118
119         // Try opening the given file
120         if(ov_fopen(filepath.c_str(), &oggFile) != 0)
121         {
122                 infostream<<"Audio: Error opening "<<filepath<<" for decoding"<<std::endl;
123                 return NULL;
124         }
125
126         SoundBuffer *snd = new SoundBuffer;
127
128         // Get some information about the OGG file
129         pInfo = ov_info(&oggFile, -1);
130
131         // Check the number of channels... always use 16-bit samples
132         if(pInfo->channels == 1)
133                 snd->format = AL_FORMAT_MONO16;
134         else
135                 snd->format = AL_FORMAT_STEREO16;
136
137         // The frequency of the sampling rate
138         snd->freq = pInfo->rate;
139
140         // Keep reading until all is read
141         do
142         {
143                 // Read up to a buffer's worth of decoded sound data
144                 bytes = ov_read(&oggFile, array, BUFFER_SIZE, endian, 2, 1, &bitStream);
145
146                 if(bytes < 0)
147                 {
148                         ov_clear(&oggFile);
149                         infostream<<"Audio: Error decoding "<<filepath<<std::endl;
150                         return NULL;
151                 }
152
153                 // Append to end of buffer
154                 snd->buffer.insert(snd->buffer.end(), array, array + bytes);
155         } while (bytes > 0);
156
157         alGenBuffers(1, &snd->buffer_id);
158         alBufferData(snd->buffer_id, snd->format,
159                         &(snd->buffer[0]), snd->buffer.size(),
160                         snd->freq);
161
162         ALenum error = alGetError();
163
164         if(error != AL_NO_ERROR){
165                 infostream<<"Audio: OpenAL error: "<<alErrorString(error)
166                                 <<"preparing sound buffer"<<std::endl;
167         }
168
169         infostream<<"Audio file "<<filepath<<" loaded"<<std::endl;
170
171         // Clean up!
172         ov_clear(&oggFile);
173
174         return snd;
175 }
176
177 struct PlayingSound
178 {
179         ALuint source_id;
180         bool loop;
181 };
182
183 class OpenALSoundManager: public ISoundManager
184 {
185 private:
186         OnDemandSoundFetcher *m_fetcher;
187         ALCdevice *m_device;
188         ALCcontext *m_context;
189         bool m_can_vorbis;
190         int m_next_id;
191         std::map<std::string, std::vector<SoundBuffer*> > m_buffers;
192         std::map<int, PlayingSound*> m_sounds_playing;
193         v3f m_listener_pos;
194 public:
195         OpenALSoundManager(OnDemandSoundFetcher *fetcher):
196                 m_fetcher(fetcher),
197                 m_device(NULL),
198                 m_context(NULL),
199                 m_can_vorbis(false),
200                 m_next_id(1)
201         {
202                 ALCenum error = ALC_NO_ERROR;
203                 
204                 infostream<<"Audio: Initializing..."<<std::endl;
205
206                 m_device = alcOpenDevice(NULL);
207                 if(!m_device){
208                         infostream<<"Audio: No audio device available, audio system "
209                                 <<"not initialized"<<std::endl;
210                         return;
211                 }
212
213                 if(alcIsExtensionPresent(m_device, "EXT_vorbis")){
214                         infostream<<"Audio: Vorbis extension present"<<std::endl;
215                         m_can_vorbis = true;
216                 } else{
217                         infostream<<"Audio: Vorbis extension NOT present"<<std::endl;
218                         m_can_vorbis = false;
219                 }
220
221                 m_context = alcCreateContext(m_device, NULL);
222                 if(!m_context){
223                         error = alcGetError(m_device);
224                         infostream<<"Audio: Unable to initialize audio context, "
225                                         <<"aborting audio initialization ("<<alcErrorString(error)
226                                         <<")"<<std::endl;
227                         alcCloseDevice(m_device);
228                         m_device = NULL;
229                         return;
230                 }
231
232                 if(!alcMakeContextCurrent(m_context) ||
233                                 (error = alcGetError(m_device) != ALC_NO_ERROR))
234                 {
235                         infostream<<"Audio: Error setting audio context, aborting audio "
236                                         <<"initialization ("<<alcErrorString(error)<<")"<<std::endl;
237                         alcDestroyContext(m_context);
238                         m_context = NULL;
239                         alcCloseDevice(m_device);
240                         m_device = NULL;
241                         return;
242                 }
243
244                 alDistanceModel(AL_EXPONENT_DISTANCE);
245
246                 infostream<<"Audio: Initialized: OpenAL "<<alGetString(AL_VERSION)
247                                 <<", using "<<alcGetString(m_device, ALC_DEVICE_SPECIFIER)
248                                 <<std::endl;
249         }
250
251         ~OpenALSoundManager()
252         {
253                 infostream<<"Audio: Deinitializing..."<<std::endl;
254                 // KABOOM!
255                 // TODO: Clear SoundBuffers
256                 alcMakeContextCurrent(NULL);
257                 alcDestroyContext(m_context);
258                 m_context = NULL;
259                 alcCloseDevice(m_device);
260                 m_device = NULL;
261                 infostream<<"Audio: Deinitialized."<<std::endl;
262         }
263         
264         void addBuffer(const std::string &name, SoundBuffer *buf)
265         {
266                 std::map<std::string, std::vector<SoundBuffer*> >::iterator i =
267                                 m_buffers.find(name);
268                 if(i != m_buffers.end()){
269                         i->second.push_back(buf);
270                         return;
271                 }
272                 std::vector<SoundBuffer*> bufs;
273                 bufs.push_back(buf);
274                 m_buffers[name] = bufs;
275                 return;
276         }
277
278         SoundBuffer* getBuffer(const std::string &name)
279         {
280                 std::map<std::string, std::vector<SoundBuffer*> >::iterator i =
281                                 m_buffers.find(name);
282                 if(i == m_buffers.end())
283                         return NULL;
284                 std::vector<SoundBuffer*> &bufs = i->second;
285                 int j = myrand() % bufs.size();
286                 return bufs[j];
287         }
288
289         PlayingSound* createPlayingSound(SoundBuffer *buf, bool loop,
290                         float volume)
291         {
292                 infostream<<"OpenALSoundManager: Creating playing sound"<<std::endl;
293                 assert(buf);
294                 PlayingSound *sound = new PlayingSound;
295                 assert(sound);
296                 warn_if_error(alGetError(), "before createPlayingSound");
297                 alGenSources(1, &sound->source_id);
298                 alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
299                 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, true);
300                 alSource3f(sound->source_id, AL_POSITION, 0, 0, 0);
301                 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
302                 alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
303                 volume = MYMAX(0.0, volume);
304                 alSourcef(sound->source_id, AL_GAIN, volume);
305                 alSourcePlay(sound->source_id);
306                 warn_if_error(alGetError(), "createPlayingSound");
307                 return sound;
308         }
309
310         PlayingSound* createPlayingSoundAt(SoundBuffer *buf, bool loop,
311                         float volume, v3f pos)
312         {
313                 infostream<<"OpenALSoundManager: Creating positional playing sound"
314                                 <<std::endl;
315                 assert(buf);
316                 PlayingSound *sound = new PlayingSound;
317                 assert(sound);
318                 warn_if_error(alGetError(), "before createPlayingSoundAt");
319                 alGenSources(1, &sound->source_id);
320                 alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
321                 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
322                 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
323                 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
324                 //alSourcef(sound->source_id, AL_ROLLOFF_FACTOR, 0.7);
325                 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0);
326                 alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
327                 volume = MYMAX(0.0, volume);
328                 alSourcef(sound->source_id, AL_GAIN, volume);
329                 alSourcePlay(sound->source_id);
330                 warn_if_error(alGetError(), "createPlayingSoundAt");
331                 return sound;
332         }
333
334         int playSoundRaw(SoundBuffer *buf, bool loop, float volume)
335         {
336                 assert(buf);
337                 PlayingSound *sound = createPlayingSound(buf, loop, volume);
338                 if(!sound)
339                         return -1;
340                 int id = m_next_id++;
341                 m_sounds_playing[id] = sound;
342                 return id;
343         }
344
345         int playSoundRawAt(SoundBuffer *buf, bool loop, float volume, v3f pos)
346         {
347                 assert(buf);
348                 PlayingSound *sound = createPlayingSoundAt(buf, loop, volume, pos);
349                 if(!sound)
350                         return -1;
351                 int id = m_next_id++;
352                 m_sounds_playing[id] = sound;
353                 return id;
354         }
355         
356         void deleteSound(int id)
357         {
358                 std::map<int, PlayingSound*>::iterator i =
359                                 m_sounds_playing.find(id);
360                 if(i == m_sounds_playing.end())
361                         return;
362                 PlayingSound *sound = i->second;
363                 
364                 alDeleteSources(1, &sound->source_id);
365
366                 delete sound;
367                 m_sounds_playing.erase(id);
368         }
369
370         /* If buffer does not exist, consult the fetcher */
371         SoundBuffer* getFetchBuffer(const std::string name)
372         {
373                 SoundBuffer *buf = getBuffer(name);
374                 if(buf)
375                         return buf;
376                 if(!m_fetcher)
377                         return NULL;
378                 std::set<std::string> paths;
379                 std::set<std::string> datas;
380                 m_fetcher->fetchSounds(name, paths, datas);
381                 for(std::set<std::string>::iterator i = paths.begin();
382                                 i != paths.end(); i++){
383                         loadSoundFile(name, *i);
384                 }
385                 for(std::set<std::string>::iterator i = datas.begin();
386                                 i != datas.end(); i++){
387                         loadSoundData(name, *i);
388                 }
389                 return getBuffer(name);
390         }
391         
392         // Remove stopped sounds
393         void maintain()
394         {
395                 verbosestream<<"OpenALSoundManager::maintain(): "
396                                 <<m_sounds_playing.size()<<" playing sounds, "
397                                 <<m_buffers.size()<<" sound names loaded"<<std::endl;
398                 std::set<int> del_list;
399                 for(std::map<int, PlayingSound*>::iterator
400                                 i = m_sounds_playing.begin();
401                                 i != m_sounds_playing.end(); i++)
402                 {
403                         int id = i->first;
404                         PlayingSound *sound = i->second;
405                         // If not playing, remove it
406                         {
407                                 ALint state;
408                                 alGetSourcei(sound->source_id, AL_SOURCE_STATE, &state);
409                                 if(state != AL_PLAYING){
410                                         del_list.insert(id);
411                                 }
412                         }
413                 }
414                 if(del_list.size() != 0)
415                         verbosestream<<"OpenALSoundManager::maintain(): deleting "
416                                         <<del_list.size()<<" playing sounds"<<std::endl;
417                 for(std::set<int>::iterator i = del_list.begin();
418                                 i != del_list.end(); i++)
419                 {
420                         deleteSound(*i);
421                 }
422         }
423
424         /* Interface */
425
426         bool loadSoundFile(const std::string &name,
427                         const std::string &filepath)
428         {
429                 SoundBuffer *buf = loadOggFile(filepath);
430                 if(buf)
431                         addBuffer(name, buf);
432                 return false;
433         }
434         bool loadSoundData(const std::string &name,
435                         const std::string &filedata)
436         {
437                 errorstream<<"OpenALSoundManager: Loading from filedata not"
438                                 " implemented"<<std::endl;
439                 return false;
440         }
441
442         void updateListener(v3f pos, v3f vel, v3f at, v3f up)
443         {
444                 m_listener_pos = pos;
445                 alListener3f(AL_POSITION, pos.X, pos.Y, pos.Z);
446                 alListener3f(AL_VELOCITY, vel.X, vel.Y, vel.Z);
447                 ALfloat f[6];
448                 f3_set(f, at);
449                 f3_set(f+3, up);
450                 alListenerfv(AL_ORIENTATION, f);
451                 warn_if_error(alGetError(), "updateListener");
452         }
453
454         int playSound(const std::string &name, bool loop, float volume)
455         {
456                 maintain();
457                 if(name == "")
458                         return 0;
459                 SoundBuffer *buf = getFetchBuffer(name);
460                 if(!buf){
461                         infostream<<"OpenALSoundManager: \""<<name<<"\" not found."
462                                         <<std::endl;
463                         return -1;
464                 }
465                 return playSoundRaw(buf, loop, volume);
466         }
467         int playSoundAt(const std::string &name, bool loop, float volume, v3f pos)
468         {
469                 maintain();
470                 if(name == "")
471                         return 0;
472                 SoundBuffer *buf = getFetchBuffer(name);
473                 if(!buf){
474                         infostream<<"OpenALSoundManager: \""<<name<<"\" not found."
475                                         <<std::endl;
476                         return -1;
477                 }
478                 return playSoundRawAt(buf, loop, volume, pos);
479         }
480         void stopSound(int sound)
481         {
482                 maintain();
483                 deleteSound(sound);
484         }
485         bool soundExists(int sound)
486         {
487                 maintain();
488                 return (m_sounds_playing.count(sound) != 0);
489         }
490         void updateSoundPosition(int id, v3f pos)
491         {
492                 std::map<int, PlayingSound*>::iterator i =
493                                 m_sounds_playing.find(id);
494                 if(i == m_sounds_playing.end())
495                         return;
496                 PlayingSound *sound = i->second;
497
498                 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
499                 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
500                 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
501                 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0);
502         }
503 };
504
505 ISoundManager *createOpenALSoundManager(OnDemandSoundFetcher *fetcher)
506 {
507         return new OpenALSoundManager(fetcher);
508 };
509