Tune caves
[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 #include "filesys.h"
45
46 #define BUFFER_SIZE 30000
47
48 static const char *alcErrorString(ALCenum err)
49 {
50         switch (err) {
51         case ALC_NO_ERROR:
52                 return "no error";
53         case ALC_INVALID_DEVICE:
54                 return "invalid device";
55         case ALC_INVALID_CONTEXT:
56                 return "invalid context";
57         case ALC_INVALID_ENUM:
58                 return "invalid enum";
59         case ALC_INVALID_VALUE:
60                 return "invalid value";
61         case ALC_OUT_OF_MEMORY:
62                 return "out of memory";
63         default:
64                 return "<unknown OpenAL error>";
65         }
66 }
67
68 static const char *alErrorString(ALenum err)
69 {
70         switch (err) {
71         case AL_NO_ERROR:
72                 return "no error";
73         case AL_INVALID_NAME:
74                 return "invalid name";
75         case AL_INVALID_ENUM:
76                 return "invalid enum";
77         case AL_INVALID_VALUE:
78                 return "invalid value";
79         case AL_INVALID_OPERATION:
80                 return "invalid operation";
81         case AL_OUT_OF_MEMORY:
82                 return "out of memory";
83         default:
84                 return "<unknown OpenAL error>";
85         }
86 }
87
88 static ALenum warn_if_error(ALenum err, const char *desc)
89 {
90         if(err == AL_NO_ERROR)
91                 return err;
92         errorstream<<"WARNING: "<<desc<<": "<<alErrorString(err)<<std::endl;
93         return err;
94 }
95
96 void f3_set(ALfloat *f3, v3f v)
97 {
98         f3[0] = v.X;
99         f3[1] = v.Y;
100         f3[2] = v.Z;
101 }
102
103 struct SoundBuffer
104 {
105         ALenum format;
106         ALsizei freq;
107         ALuint buffer_id;
108         std::vector<char> buffer;
109 };
110
111 SoundBuffer* loadOggFile(const std::string &filepath)
112 {
113         int endian = 0; // 0 for Little-Endian, 1 for Big-Endian
114         int bitStream;
115         long bytes;
116         char array[BUFFER_SIZE]; // Local fixed size array
117         vorbis_info *pInfo;
118         OggVorbis_File oggFile;
119         
120         // Do a dumb-ass static string copy for old versions of ov_fopen
121         // because they expect a non-const char*
122         char nonconst[10000];
123         snprintf(nonconst, 10000, "%s", filepath.c_str());
124         // Try opening the given file
125         //if(ov_fopen(filepath.c_str(), &oggFile) != 0)
126         if(ov_fopen(nonconst, &oggFile) != 0)
127         {
128                 infostream<<"Audio: Error opening "<<filepath<<" for decoding"<<std::endl;
129                 return NULL;
130         }
131
132         SoundBuffer *snd = new SoundBuffer;
133
134         // Get some information about the OGG file
135         pInfo = ov_info(&oggFile, -1);
136
137         // Check the number of channels... always use 16-bit samples
138         if(pInfo->channels == 1)
139                 snd->format = AL_FORMAT_MONO16;
140         else
141                 snd->format = AL_FORMAT_STEREO16;
142
143         // The frequency of the sampling rate
144         snd->freq = pInfo->rate;
145
146         // Keep reading until all is read
147         do
148         {
149                 // Read up to a buffer's worth of decoded sound data
150                 bytes = ov_read(&oggFile, array, BUFFER_SIZE, endian, 2, 1, &bitStream);
151
152                 if(bytes < 0)
153                 {
154                         ov_clear(&oggFile);
155                         infostream<<"Audio: Error decoding "<<filepath<<std::endl;
156                         return NULL;
157                 }
158
159                 // Append to end of buffer
160                 snd->buffer.insert(snd->buffer.end(), array, array + bytes);
161         } while (bytes > 0);
162
163         alGenBuffers(1, &snd->buffer_id);
164         alBufferData(snd->buffer_id, snd->format,
165                         &(snd->buffer[0]), snd->buffer.size(),
166                         snd->freq);
167
168         ALenum error = alGetError();
169
170         if(error != AL_NO_ERROR){
171                 infostream<<"Audio: OpenAL error: "<<alErrorString(error)
172                                 <<"preparing sound buffer"<<std::endl;
173         }
174
175         infostream<<"Audio file "<<filepath<<" loaded"<<std::endl;
176
177         // Clean up!
178         ov_clear(&oggFile);
179
180         return snd;
181 }
182
183 struct PlayingSound
184 {
185         ALuint source_id;
186         bool loop;
187 };
188
189 class OpenALSoundManager: public ISoundManager
190 {
191 private:
192         OnDemandSoundFetcher *m_fetcher;
193         ALCdevice *m_device;
194         ALCcontext *m_context;
195         bool m_can_vorbis;
196         int m_next_id;
197         std::map<std::string, std::vector<SoundBuffer*> > m_buffers;
198         std::map<int, PlayingSound*> m_sounds_playing;
199         v3f m_listener_pos;
200 public:
201         OpenALSoundManager(OnDemandSoundFetcher *fetcher):
202                 m_fetcher(fetcher),
203                 m_device(NULL),
204                 m_context(NULL),
205                 m_can_vorbis(false),
206                 m_next_id(1)
207         {
208                 ALCenum error = ALC_NO_ERROR;
209                 
210                 infostream<<"Audio: Initializing..."<<std::endl;
211
212                 m_device = alcOpenDevice(NULL);
213                 if(!m_device){
214                         infostream<<"Audio: No audio device available, audio system "
215                                 <<"not initialized"<<std::endl;
216                         return;
217                 }
218
219                 if(alcIsExtensionPresent(m_device, "EXT_vorbis")){
220                         infostream<<"Audio: Vorbis extension present"<<std::endl;
221                         m_can_vorbis = true;
222                 } else{
223                         infostream<<"Audio: Vorbis extension NOT present"<<std::endl;
224                         m_can_vorbis = false;
225                 }
226
227                 m_context = alcCreateContext(m_device, NULL);
228                 if(!m_context){
229                         error = alcGetError(m_device);
230                         infostream<<"Audio: Unable to initialize audio context, "
231                                         <<"aborting audio initialization ("<<alcErrorString(error)
232                                         <<")"<<std::endl;
233                         alcCloseDevice(m_device);
234                         m_device = NULL;
235                         return;
236                 }
237
238                 if(!alcMakeContextCurrent(m_context) ||
239                                 (error = alcGetError(m_device) != ALC_NO_ERROR))
240                 {
241                         infostream<<"Audio: Error setting audio context, aborting audio "
242                                         <<"initialization ("<<alcErrorString(error)<<")"<<std::endl;
243                         alcDestroyContext(m_context);
244                         m_context = NULL;
245                         alcCloseDevice(m_device);
246                         m_device = NULL;
247                         return;
248                 }
249
250                 alDistanceModel(AL_EXPONENT_DISTANCE);
251
252                 infostream<<"Audio: Initialized: OpenAL "<<alGetString(AL_VERSION)
253                                 <<", using "<<alcGetString(m_device, ALC_DEVICE_SPECIFIER)
254                                 <<std::endl;
255         }
256
257         ~OpenALSoundManager()
258         {
259                 infostream<<"Audio: Deinitializing..."<<std::endl;
260                 // KABOOM!
261                 // TODO: Clear SoundBuffers
262                 alcMakeContextCurrent(NULL);
263                 alcDestroyContext(m_context);
264                 m_context = NULL;
265                 alcCloseDevice(m_device);
266                 m_device = NULL;
267                 infostream<<"Audio: Deinitialized."<<std::endl;
268         }
269         
270         void addBuffer(const std::string &name, SoundBuffer *buf)
271         {
272                 std::map<std::string, std::vector<SoundBuffer*> >::iterator i =
273                                 m_buffers.find(name);
274                 if(i != m_buffers.end()){
275                         i->second.push_back(buf);
276                         return;
277                 }
278                 std::vector<SoundBuffer*> bufs;
279                 bufs.push_back(buf);
280                 m_buffers[name] = bufs;
281                 return;
282         }
283
284         SoundBuffer* getBuffer(const std::string &name)
285         {
286                 std::map<std::string, std::vector<SoundBuffer*> >::iterator i =
287                                 m_buffers.find(name);
288                 if(i == m_buffers.end())
289                         return NULL;
290                 std::vector<SoundBuffer*> &bufs = i->second;
291                 int j = myrand() % bufs.size();
292                 return bufs[j];
293         }
294
295         PlayingSound* createPlayingSound(SoundBuffer *buf, bool loop,
296                         float volume)
297         {
298                 infostream<<"OpenALSoundManager: Creating playing sound"<<std::endl;
299                 assert(buf);
300                 PlayingSound *sound = new PlayingSound;
301                 assert(sound);
302                 warn_if_error(alGetError(), "before createPlayingSound");
303                 alGenSources(1, &sound->source_id);
304                 alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
305                 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, true);
306                 alSource3f(sound->source_id, AL_POSITION, 0, 0, 0);
307                 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
308                 alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
309                 volume = MYMAX(0.0, volume);
310                 alSourcef(sound->source_id, AL_GAIN, volume);
311                 alSourcePlay(sound->source_id);
312                 warn_if_error(alGetError(), "createPlayingSound");
313                 return sound;
314         }
315
316         PlayingSound* createPlayingSoundAt(SoundBuffer *buf, bool loop,
317                         float volume, v3f pos)
318         {
319                 infostream<<"OpenALSoundManager: Creating positional playing sound"
320                                 <<std::endl;
321                 assert(buf);
322                 PlayingSound *sound = new PlayingSound;
323                 assert(sound);
324                 warn_if_error(alGetError(), "before createPlayingSoundAt");
325                 alGenSources(1, &sound->source_id);
326                 alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
327                 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
328                 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
329                 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
330                 //alSourcef(sound->source_id, AL_ROLLOFF_FACTOR, 0.7);
331                 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0);
332                 alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
333                 volume = MYMAX(0.0, volume);
334                 alSourcef(sound->source_id, AL_GAIN, volume);
335                 alSourcePlay(sound->source_id);
336                 warn_if_error(alGetError(), "createPlayingSoundAt");
337                 return sound;
338         }
339
340         int playSoundRaw(SoundBuffer *buf, bool loop, float volume)
341         {
342                 assert(buf);
343                 PlayingSound *sound = createPlayingSound(buf, loop, volume);
344                 if(!sound)
345                         return -1;
346                 int id = m_next_id++;
347                 m_sounds_playing[id] = sound;
348                 return id;
349         }
350
351         int playSoundRawAt(SoundBuffer *buf, bool loop, float volume, v3f pos)
352         {
353                 assert(buf);
354                 PlayingSound *sound = createPlayingSoundAt(buf, loop, volume, pos);
355                 if(!sound)
356                         return -1;
357                 int id = m_next_id++;
358                 m_sounds_playing[id] = sound;
359                 return id;
360         }
361         
362         void deleteSound(int id)
363         {
364                 std::map<int, PlayingSound*>::iterator i =
365                                 m_sounds_playing.find(id);
366                 if(i == m_sounds_playing.end())
367                         return;
368                 PlayingSound *sound = i->second;
369                 
370                 alDeleteSources(1, &sound->source_id);
371
372                 delete sound;
373                 m_sounds_playing.erase(id);
374         }
375
376         /* If buffer does not exist, consult the fetcher */
377         SoundBuffer* getFetchBuffer(const std::string name)
378         {
379                 SoundBuffer *buf = getBuffer(name);
380                 if(buf)
381                         return buf;
382                 if(!m_fetcher)
383                         return NULL;
384                 std::set<std::string> paths;
385                 std::set<std::string> datas;
386                 m_fetcher->fetchSounds(name, paths, datas);
387                 for(std::set<std::string>::iterator i = paths.begin();
388                                 i != paths.end(); i++){
389                         loadSoundFile(name, *i);
390                 }
391                 for(std::set<std::string>::iterator i = datas.begin();
392                                 i != datas.end(); i++){
393                         loadSoundData(name, *i);
394                 }
395                 return getBuffer(name);
396         }
397         
398         // Remove stopped sounds
399         void maintain()
400         {
401                 verbosestream<<"OpenALSoundManager::maintain(): "
402                                 <<m_sounds_playing.size()<<" playing sounds, "
403                                 <<m_buffers.size()<<" sound names loaded"<<std::endl;
404                 std::set<int> del_list;
405                 for(std::map<int, PlayingSound*>::iterator
406                                 i = m_sounds_playing.begin();
407                                 i != m_sounds_playing.end(); i++)
408                 {
409                         int id = i->first;
410                         PlayingSound *sound = i->second;
411                         // If not playing, remove it
412                         {
413                                 ALint state;
414                                 alGetSourcei(sound->source_id, AL_SOURCE_STATE, &state);
415                                 if(state != AL_PLAYING){
416                                         del_list.insert(id);
417                                 }
418                         }
419                 }
420                 if(del_list.size() != 0)
421                         verbosestream<<"OpenALSoundManager::maintain(): deleting "
422                                         <<del_list.size()<<" playing sounds"<<std::endl;
423                 for(std::set<int>::iterator i = del_list.begin();
424                                 i != del_list.end(); i++)
425                 {
426                         deleteSound(*i);
427                 }
428         }
429
430         /* Interface */
431
432         bool loadSoundFile(const std::string &name,
433                         const std::string &filepath)
434         {
435                 SoundBuffer *buf = loadOggFile(filepath);
436                 if(buf)
437                         addBuffer(name, buf);
438                 return false;
439         }
440         bool loadSoundData(const std::string &name,
441                         const std::string &filedata)
442         {
443                 // The vorbis API sucks; just write it to a file and use vorbisfile
444                 // TODO: Actually load it directly from memory
445                 std::string basepath = porting::path_user + DIR_DELIM + "cache" +
446                                 DIR_DELIM + "tmp";
447                 std::string path = basepath + DIR_DELIM + "tmp.ogg";
448                 verbosestream<<"OpenALSoundManager::loadSoundData(): Writing "
449                                 <<"temporary file to ["<<path<<"]"<<std::endl;
450                 fs::CreateAllDirs(basepath);
451                 std::ofstream of(path.c_str(), std::ios::binary);
452                 of.write(filedata.c_str(), filedata.size());
453                 of.close();
454                 return loadSoundFile(name, path);
455         }
456
457         void updateListener(v3f pos, v3f vel, v3f at, v3f up)
458         {
459                 m_listener_pos = pos;
460                 alListener3f(AL_POSITION, pos.X, pos.Y, pos.Z);
461                 alListener3f(AL_VELOCITY, vel.X, vel.Y, vel.Z);
462                 ALfloat f[6];
463                 f3_set(f, at);
464                 f3_set(f+3, up);
465                 alListenerfv(AL_ORIENTATION, f);
466                 warn_if_error(alGetError(), "updateListener");
467         }
468
469         int playSound(const std::string &name, bool loop, float volume)
470         {
471                 maintain();
472                 if(name == "")
473                         return 0;
474                 SoundBuffer *buf = getFetchBuffer(name);
475                 if(!buf){
476                         infostream<<"OpenALSoundManager: \""<<name<<"\" not found."
477                                         <<std::endl;
478                         return -1;
479                 }
480                 return playSoundRaw(buf, loop, volume);
481         }
482         int playSoundAt(const std::string &name, bool loop, float volume, v3f pos)
483         {
484                 maintain();
485                 if(name == "")
486                         return 0;
487                 SoundBuffer *buf = getFetchBuffer(name);
488                 if(!buf){
489                         infostream<<"OpenALSoundManager: \""<<name<<"\" not found."
490                                         <<std::endl;
491                         return -1;
492                 }
493                 return playSoundRawAt(buf, loop, volume, pos);
494         }
495         void stopSound(int sound)
496         {
497                 maintain();
498                 deleteSound(sound);
499         }
500         bool soundExists(int sound)
501         {
502                 maintain();
503                 return (m_sounds_playing.count(sound) != 0);
504         }
505         void updateSoundPosition(int id, v3f pos)
506         {
507                 std::map<int, PlayingSound*>::iterator i =
508                                 m_sounds_playing.find(id);
509                 if(i == m_sounds_playing.end())
510                         return;
511                 PlayingSound *sound = i->second;
512
513                 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
514                 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
515                 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
516                 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0);
517         }
518 };
519
520 ISoundManager *createOpenALSoundManager(OnDemandSoundFetcher *fetcher)
521 {
522         return new OpenALSoundManager(fetcher);
523 };
524