Fix a crash or random memory leak when reseting saved environment variable in test_se...
[oweals/minetest.git] / src / sound_openal.cpp
1 /*
2 Minetest
3 Copyright (C) 2013 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 Lesser General Public License as published by
11 the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
18
19 You should have received a copy of the GNU Lesser 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 <cmath>
40 #include <vorbis/vorbisfile.h>
41 #include <cassert>
42 #include "log.h"
43 #include "util/numeric.h" // myrand()
44 #include "porting.h"
45 #include <vector>
46 #include <fstream>
47 #include <unordered_map>
48
49 #define BUFFER_SIZE 30000
50
51 static const char *alcErrorString(ALCenum err)
52 {
53         switch (err) {
54         case ALC_NO_ERROR:
55                 return "no error";
56         case ALC_INVALID_DEVICE:
57                 return "invalid device";
58         case ALC_INVALID_CONTEXT:
59                 return "invalid context";
60         case ALC_INVALID_ENUM:
61                 return "invalid enum";
62         case ALC_INVALID_VALUE:
63                 return "invalid value";
64         case ALC_OUT_OF_MEMORY:
65                 return "out of memory";
66         default:
67                 return "<unknown OpenAL error>";
68         }
69 }
70
71 static const char *alErrorString(ALenum err)
72 {
73         switch (err) {
74         case AL_NO_ERROR:
75                 return "no error";
76         case AL_INVALID_NAME:
77                 return "invalid name";
78         case AL_INVALID_ENUM:
79                 return "invalid enum";
80         case AL_INVALID_VALUE:
81                 return "invalid value";
82         case AL_INVALID_OPERATION:
83                 return "invalid operation";
84         case AL_OUT_OF_MEMORY:
85                 return "out of memory";
86         default:
87                 return "<unknown OpenAL error>";
88         }
89 }
90
91 static ALenum warn_if_error(ALenum err, const char *desc)
92 {
93         if(err == AL_NO_ERROR)
94                 return err;
95         warningstream<<desc<<": "<<alErrorString(err)<<std::endl;
96         return err;
97 }
98
99 void f3_set(ALfloat *f3, v3f v)
100 {
101         f3[0] = v.X;
102         f3[1] = v.Y;
103         f3[2] = v.Z;
104 }
105
106 struct SoundBuffer
107 {
108         ALenum format;
109         ALsizei freq;
110         ALuint buffer_id;
111         std::vector<char> buffer;
112 };
113
114 SoundBuffer *load_opened_ogg_file(OggVorbis_File *oggFile,
115                 const std::string &filename_for_logging)
116 {
117         int endian = 0; // 0 for Little-Endian, 1 for Big-Endian
118         int bitStream;
119         long bytes;
120         char array[BUFFER_SIZE]; // Local fixed size array
121         vorbis_info *pInfo;
122
123         SoundBuffer *snd = new SoundBuffer;
124
125         // Get some information about the OGG file
126         pInfo = ov_info(oggFile, -1);
127
128         // Check the number of channels... always use 16-bit samples
129         if(pInfo->channels == 1)
130                 snd->format = AL_FORMAT_MONO16;
131         else
132                 snd->format = AL_FORMAT_STEREO16;
133
134         // The frequency of the sampling rate
135         snd->freq = pInfo->rate;
136
137         // Keep reading until all is read
138         do
139         {
140                 // Read up to a buffer's worth of decoded sound data
141                 bytes = ov_read(oggFile, array, BUFFER_SIZE, endian, 2, 1, &bitStream);
142
143                 if(bytes < 0)
144                 {
145                         ov_clear(oggFile);
146                         infostream << "Audio: Error decoding "
147                                 << filename_for_logging << std::endl;
148                         delete snd;
149                         return NULL;
150                 }
151
152                 // Append to end of buffer
153                 snd->buffer.insert(snd->buffer.end(), array, array + bytes);
154         } while (bytes > 0);
155
156         alGenBuffers(1, &snd->buffer_id);
157         alBufferData(snd->buffer_id, snd->format,
158                         &(snd->buffer[0]), snd->buffer.size(),
159                         snd->freq);
160
161         ALenum error = alGetError();
162
163         if(error != AL_NO_ERROR){
164                 infostream<<"Audio: OpenAL error: "<<alErrorString(error)
165                                 <<"preparing sound buffer"<<std::endl;
166         }
167
168         infostream << "Audio file "
169                 << filename_for_logging << " loaded" << std::endl;
170
171         // Clean up!
172         ov_clear(oggFile);
173
174         return snd;
175 }
176
177 SoundBuffer *load_ogg_from_file(const std::string &path)
178 {
179         OggVorbis_File oggFile;
180
181         // Try opening the given file.
182         // This requires libvorbis >= 1.3.2, as
183         // previous versions expect a non-const char *
184         if (ov_fopen(path.c_str(), &oggFile) != 0) {
185                 infostream << "Audio: Error opening " << path
186                         << " for decoding" << std::endl;
187                 return NULL;
188         }
189
190         return load_opened_ogg_file(&oggFile, path);
191 }
192
193 struct BufferSource {
194         const char *buf;
195         size_t cur_offset;
196         size_t len;
197 };
198
199 size_t buffer_sound_read_func(void *ptr, size_t size, size_t nmemb, void *datasource)
200 {
201         BufferSource *s = (BufferSource *)datasource;
202         size_t copied_size = MYMIN(s->len - s->cur_offset, size);
203         memcpy(ptr, s->buf + s->cur_offset, copied_size);
204         s->cur_offset += copied_size;
205         return copied_size;
206 }
207
208 int buffer_sound_seek_func(void *datasource, ogg_int64_t offset, int whence)
209 {
210         BufferSource *s = (BufferSource *)datasource;
211         if (whence == SEEK_SET) {
212                 if (offset < 0 || (size_t)MYMAX(offset, 0) >= s->len) {
213                         // offset out of bounds
214                         return -1;
215                 }
216                 s->cur_offset = offset;
217                 return 0;
218         } else if (whence == SEEK_CUR) {
219                 if ((size_t)MYMIN(-offset, 0) > s->cur_offset
220                                 || s->cur_offset + offset > s->len) {
221                         // offset out of bounds
222                         return -1;
223                 }
224                 s->cur_offset += offset;
225                 return 0;
226         }
227         // invalid whence param (SEEK_END doesn't have to be supported)
228         return -1;
229 }
230
231 long BufferSourceell_func(void *datasource)
232 {
233         BufferSource *s = (BufferSource *)datasource;
234         return s->cur_offset;
235 }
236
237 static ov_callbacks g_buffer_ov_callbacks = {
238         &buffer_sound_read_func,
239         &buffer_sound_seek_func,
240         NULL,
241         &BufferSourceell_func
242 };
243
244 SoundBuffer *load_ogg_from_buffer(const std::string &buf, const std::string &id_for_log)
245 {
246         OggVorbis_File oggFile;
247
248         BufferSource s;
249         s.buf = buf.c_str();
250         s.cur_offset = 0;
251         s.len = buf.size();
252
253         if (ov_open_callbacks(&s, &oggFile, NULL, 0, g_buffer_ov_callbacks) != 0) {
254                 infostream << "Audio: Error opening " << id_for_log
255                         << " for decoding" << std::endl;
256                 return NULL;
257         }
258
259         return load_opened_ogg_file(&oggFile, id_for_log);
260 }
261
262 struct PlayingSound
263 {
264         ALuint source_id;
265         bool loop;
266 };
267
268 class OpenALSoundManager: public ISoundManager
269 {
270 private:
271         OnDemandSoundFetcher *m_fetcher;
272         ALCdevice *m_device;
273         ALCcontext *m_context;
274         int m_next_id;
275         std::unordered_map<std::string, std::vector<SoundBuffer*>> m_buffers;
276         std::unordered_map<int, PlayingSound*> m_sounds_playing;
277         v3f m_listener_pos;
278         struct FadeState {
279                 FadeState() = default;
280
281                 FadeState(float step, float current_gain, float target_gain):
282                         step(step),
283                         current_gain(current_gain),
284                         target_gain(target_gain) {}
285                 float step;
286                 float current_gain;
287                 float target_gain;
288         };
289
290         std::unordered_map<int, FadeState> m_sounds_fading;
291         float m_fade_delay;
292 public:
293         bool m_is_initialized;
294         OpenALSoundManager(OnDemandSoundFetcher *fetcher):
295                 m_fetcher(fetcher),
296                 m_device(NULL),
297                 m_context(NULL),
298                 m_next_id(1),
299                 m_fade_delay(0),
300                 m_is_initialized(false)
301         {
302                 ALCenum error = ALC_NO_ERROR;
303
304                 infostream<<"Audio: Initializing..."<<std::endl;
305
306                 m_device = alcOpenDevice(NULL);
307                 if(!m_device){
308                         infostream<<"Audio: No audio device available, audio system "
309                                 <<"not initialized"<<std::endl;
310                         return;
311                 }
312
313                 m_context = alcCreateContext(m_device, NULL);
314                 if(!m_context){
315                         error = alcGetError(m_device);
316                         infostream<<"Audio: Unable to initialize audio context, "
317                                         <<"aborting audio initialization ("<<alcErrorString(error)
318                                         <<")"<<std::endl;
319                         alcCloseDevice(m_device);
320                         m_device = NULL;
321                         return;
322                 }
323
324                 if(!alcMakeContextCurrent(m_context) ||
325                                 (error = alcGetError(m_device) != ALC_NO_ERROR))
326                 {
327                         infostream<<"Audio: Error setting audio context, aborting audio "
328                                         <<"initialization ("<<alcErrorString(error)<<")"<<std::endl;
329                         alcDestroyContext(m_context);
330                         m_context = NULL;
331                         alcCloseDevice(m_device);
332                         m_device = NULL;
333                         return;
334                 }
335
336                 alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);
337
338                 infostream<<"Audio: Initialized: OpenAL "<<alGetString(AL_VERSION)
339                                 <<", using "<<alcGetString(m_device, ALC_DEVICE_SPECIFIER)
340                                 <<std::endl;
341
342                 m_is_initialized = true;
343         }
344
345         ~OpenALSoundManager()
346         {
347                 infostream<<"Audio: Deinitializing..."<<std::endl;
348                 // KABOOM!
349                 // TODO: Clear SoundBuffers
350                 alcMakeContextCurrent(NULL);
351                 alcDestroyContext(m_context);
352                 m_context = NULL;
353                 alcCloseDevice(m_device);
354                 m_device = NULL;
355
356                 for (auto &buffer : m_buffers) {
357                         for (SoundBuffer *sb : buffer.second) {
358                                 delete sb;
359                         }
360                         buffer.second.clear();
361                 }
362                 m_buffers.clear();
363                 infostream<<"Audio: Deinitialized."<<std::endl;
364         }
365
366         void step(float dtime)
367         {
368                 doFades(dtime);
369         }
370
371         void addBuffer(const std::string &name, SoundBuffer *buf)
372         {
373                 std::unordered_map<std::string, std::vector<SoundBuffer*>>::iterator i =
374                                 m_buffers.find(name);
375                 if(i != m_buffers.end()){
376                         i->second.push_back(buf);
377                         return;
378                 }
379                 std::vector<SoundBuffer*> bufs;
380                 bufs.push_back(buf);
381                 m_buffers[name] = bufs;
382         }
383
384         SoundBuffer* getBuffer(const std::string &name)
385         {
386                 std::unordered_map<std::string, std::vector<SoundBuffer*>>::iterator i =
387                                 m_buffers.find(name);
388                 if(i == m_buffers.end())
389                         return NULL;
390                 std::vector<SoundBuffer*> &bufs = i->second;
391                 int j = myrand() % bufs.size();
392                 return bufs[j];
393         }
394
395         PlayingSound* createPlayingSound(SoundBuffer *buf, bool loop,
396                         float volume, float pitch)
397         {
398                 infostream<<"OpenALSoundManager: Creating playing sound"<<std::endl;
399                 assert(buf);
400                 PlayingSound *sound = new PlayingSound;
401                 assert(sound);
402                 warn_if_error(alGetError(), "before createPlayingSound");
403                 alGenSources(1, &sound->source_id);
404                 alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
405                 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, true);
406                 alSource3f(sound->source_id, AL_POSITION, 0, 0, 0);
407                 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
408                 alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
409                 volume = std::fmax(0.0f, volume);
410                 alSourcef(sound->source_id, AL_GAIN, volume);
411                 alSourcef(sound->source_id, AL_PITCH, pitch);
412                 alSourcePlay(sound->source_id);
413                 warn_if_error(alGetError(), "createPlayingSound");
414                 return sound;
415         }
416
417         PlayingSound* createPlayingSoundAt(SoundBuffer *buf, bool loop,
418                         float volume, v3f pos, float pitch)
419         {
420                 infostream<<"OpenALSoundManager: Creating positional playing sound"
421                                 <<std::endl;
422                 assert(buf);
423                 PlayingSound *sound = new PlayingSound;
424                 assert(sound);
425                 warn_if_error(alGetError(), "before createPlayingSoundAt");
426                 alGenSources(1, &sound->source_id);
427                 alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
428                 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
429                 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
430                 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
431                 // Use alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED) and set reference
432                 // distance to clamp gain at <1 node distance, to avoid excessive
433                 // volume when closer
434                 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 10.0f);
435                 alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
436                 // Multiply by 3 to compensate for reducing AL_REFERENCE_DISTANCE from
437                 // the previous value of 30 to the new value of 10
438                 volume = std::fmax(0.0f, volume * 3.0f);
439                 alSourcef(sound->source_id, AL_GAIN, volume);
440                 alSourcef(sound->source_id, AL_PITCH, pitch);
441                 alSourcePlay(sound->source_id);
442                 warn_if_error(alGetError(), "createPlayingSoundAt");
443                 return sound;
444         }
445
446         int playSoundRaw(SoundBuffer *buf, bool loop, float volume, float pitch)
447         {
448                 assert(buf);
449                 PlayingSound *sound = createPlayingSound(buf, loop, volume, pitch);
450                 if(!sound)
451                         return -1;
452                 int id = m_next_id++;
453                 m_sounds_playing[id] = sound;
454                 return id;
455         }
456
457         int playSoundRawAt(SoundBuffer *buf, bool loop, float volume, const v3f &pos,
458                         float pitch)
459         {
460                 assert(buf);
461                 PlayingSound *sound = createPlayingSoundAt(buf, loop, volume, pos, pitch);
462                 if(!sound)
463                         return -1;
464                 int id = m_next_id++;
465                 m_sounds_playing[id] = sound;
466                 return id;
467         }
468
469         void deleteSound(int id)
470         {
471                 std::unordered_map<int, PlayingSound*>::iterator i = m_sounds_playing.find(id);
472                 if(i == m_sounds_playing.end())
473                         return;
474                 PlayingSound *sound = i->second;
475
476                 alDeleteSources(1, &sound->source_id);
477
478                 delete sound;
479                 m_sounds_playing.erase(id);
480         }
481
482         /* If buffer does not exist, consult the fetcher */
483         SoundBuffer* getFetchBuffer(const std::string &name)
484         {
485                 SoundBuffer *buf = getBuffer(name);
486                 if(buf)
487                         return buf;
488                 if(!m_fetcher)
489                         return NULL;
490                 std::set<std::string> paths;
491                 std::set<std::string> datas;
492                 m_fetcher->fetchSounds(name, paths, datas);
493                 for (const std::string &path : paths) {
494                         loadSoundFile(name, path);
495                 }
496                 for (const std::string &data : datas) {
497                         loadSoundData(name, data);
498                 }
499                 return getBuffer(name);
500         }
501
502         // Remove stopped sounds
503         void maintain()
504         {
505                 verbosestream<<"OpenALSoundManager::maintain(): "
506                                 <<m_sounds_playing.size()<<" playing sounds, "
507                                 <<m_buffers.size()<<" sound names loaded"<<std::endl;
508                 std::set<int> del_list;
509                 for (auto &sp : m_sounds_playing) {
510                         int id = sp.first;
511                         PlayingSound *sound = sp.second;
512                         // If not playing, remove it
513                         {
514                                 ALint state;
515                                 alGetSourcei(sound->source_id, AL_SOURCE_STATE, &state);
516                                 if(state != AL_PLAYING){
517                                         del_list.insert(id);
518                                 }
519                         }
520                 }
521                 if(!del_list.empty())
522                         verbosestream<<"OpenALSoundManager::maintain(): deleting "
523                                         <<del_list.size()<<" playing sounds"<<std::endl;
524                 for (int i : del_list) {
525                         deleteSound(i);
526                 }
527         }
528
529         /* Interface */
530
531         bool loadSoundFile(const std::string &name,
532                         const std::string &filepath)
533         {
534                 SoundBuffer *buf = load_ogg_from_file(filepath);
535                 if (buf)
536                         addBuffer(name, buf);
537                 return false;
538         }
539
540         bool loadSoundData(const std::string &name,
541                         const std::string &filedata)
542         {
543                 SoundBuffer *buf = load_ogg_from_buffer(filedata, name);
544                 if (buf)
545                         addBuffer(name, buf);
546                 return false;
547         }
548
549         void updateListener(v3f pos, v3f vel, v3f at, v3f up)
550         {
551                 m_listener_pos = pos;
552                 alListener3f(AL_POSITION, pos.X, pos.Y, pos.Z);
553                 alListener3f(AL_VELOCITY, vel.X, vel.Y, vel.Z);
554                 ALfloat f[6];
555                 f3_set(f, at);
556                 f3_set(f+3, -up);
557                 alListenerfv(AL_ORIENTATION, f);
558                 warn_if_error(alGetError(), "updateListener");
559         }
560
561         void setListenerGain(float gain)
562         {
563                 alListenerf(AL_GAIN, gain);
564         }
565
566         int playSound(const std::string &name, bool loop, float volume, float fade, float pitch)
567         {
568                 maintain();
569                 if (name.empty())
570                         return 0;
571                 SoundBuffer *buf = getFetchBuffer(name);
572                 if(!buf){
573                         infostream<<"OpenALSoundManager: \""<<name<<"\" not found."
574                                         <<std::endl;
575                         return -1;
576                 }
577                 int handle = -1;
578                 if (fade > 0) {
579                         handle = playSoundRaw(buf, loop, 0.0f, pitch);
580                         fadeSound(handle, fade, volume);
581                 } else {
582                         handle = playSoundRaw(buf, loop, volume, pitch);
583                 }
584                 return handle;
585         }
586
587         int playSoundAt(const std::string &name, bool loop, float volume, v3f pos, float pitch)
588         {
589                 maintain();
590                 if (name.empty())
591                         return 0;
592                 SoundBuffer *buf = getFetchBuffer(name);
593                 if(!buf){
594                         infostream<<"OpenALSoundManager: \""<<name<<"\" not found."
595                                         <<std::endl;
596                         return -1;
597                 }
598                 return playSoundRawAt(buf, loop, volume, pos, pitch);
599         }
600
601         void stopSound(int sound)
602         {
603                 maintain();
604                 deleteSound(sound);
605         }
606
607         void fadeSound(int soundid, float step, float gain)
608         {
609                 m_sounds_fading[soundid] = FadeState(step, getSoundGain(soundid), gain);
610         }
611
612         void doFades(float dtime)
613         {
614                 m_fade_delay += dtime;
615
616                 if (m_fade_delay < 0.1f)
617                         return;
618
619                 float chkGain = 0;
620                 for (std::unordered_map<int, FadeState>::iterator i = m_sounds_fading.begin();
621                                 i != m_sounds_fading.end();) {
622                         if (i->second.step < 0.f)
623                                 chkGain = -(i->second.current_gain);
624                         else
625                                 chkGain = i->second.current_gain;
626
627                         if (chkGain < i->second.target_gain) {
628                                 i->second.current_gain += (i->second.step * m_fade_delay);
629                                 i->second.current_gain = rangelim(i->second.current_gain, 0, 1);
630
631                                 updateSoundGain(i->first, i->second.current_gain);
632                                 ++i;
633                         } else {
634                                 if (i->second.target_gain <= 0.f)
635                                         stopSound(i->first);
636
637                                 m_sounds_fading.erase(i++);
638                         }
639                 }
640                 m_fade_delay = 0;
641         }
642
643         bool soundExists(int sound)
644         {
645                 maintain();
646                 return (m_sounds_playing.count(sound) != 0);
647         }
648
649         void updateSoundPosition(int id, v3f pos)
650         {
651                 std::unordered_map<int, PlayingSound*>::iterator i = m_sounds_playing.find(id);
652                 if (i == m_sounds_playing.end())
653                         return;
654                 PlayingSound *sound = i->second;
655
656                 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
657                 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
658                 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
659                 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0);
660         }
661
662         bool updateSoundGain(int id, float gain)
663         {
664                 std::unordered_map<int, PlayingSound*>::iterator i = m_sounds_playing.find(id);
665                 if (i == m_sounds_playing.end())
666                         return false;
667
668                 PlayingSound *sound = i->second;
669                 alSourcef(sound->source_id, AL_GAIN, gain);
670                 return true;
671         }
672
673         float getSoundGain(int id)
674         {
675                 std::unordered_map<int, PlayingSound*>::iterator i = m_sounds_playing.find(id);
676                 if (i == m_sounds_playing.end())
677                         return 0;
678
679                 PlayingSound *sound = i->second;
680                 ALfloat gain;
681                 alGetSourcef(sound->source_id, AL_GAIN, &gain);
682                 return gain;
683         }
684 };
685
686 ISoundManager *createOpenALSoundManager(OnDemandSoundFetcher *fetcher)
687 {
688         OpenALSoundManager *m = new OpenALSoundManager(fetcher);
689         if(m->m_is_initialized)
690                 return m;
691         delete m;
692         return NULL;
693 };
694