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