Revert "Revert CSM particles commit to fix particle spawner bug for 5.0.0 (#8288)"
[oweals/minetest.git] / src / client / 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 #include <unordered_set>
49
50 #define BUFFER_SIZE 30000
51
52 std::shared_ptr<SoundManagerSingleton> g_sound_manager_singleton;
53
54 typedef std::unique_ptr<ALCdevice, void (*)(ALCdevice *p)> unique_ptr_alcdevice;
55 typedef std::unique_ptr<ALCcontext, void(*)(ALCcontext *p)> unique_ptr_alccontext;
56
57 static void delete_alcdevice(ALCdevice *p)
58 {
59         if (p)
60                 alcCloseDevice(p);
61 }
62
63 static void delete_alccontext(ALCcontext *p)
64 {
65         if (p) {
66                 alcMakeContextCurrent(nullptr);
67                 alcDestroyContext(p);
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 nullptr;
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 nullptr;
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         nullptr,
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, nullptr, 0, g_buffer_ov_callbacks) != 0) {
254                 infostream << "Audio: Error opening " << id_for_log
255                         << " for decoding" << std::endl;
256                 return nullptr;
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 SoundManagerSingleton
269 {
270 public:
271         unique_ptr_alcdevice  m_device;
272         unique_ptr_alccontext m_context;
273 public:
274         SoundManagerSingleton() :
275                 m_device(nullptr, delete_alcdevice),
276                 m_context(nullptr, delete_alccontext)
277         {
278                 if (!(m_device = unique_ptr_alcdevice(alcOpenDevice(nullptr), delete_alcdevice)))
279                         throw std::runtime_error("Audio: Global Initialization: Device Open");
280
281                 if (!(m_context = unique_ptr_alccontext(
282                                 alcCreateContext(m_device.get(), nullptr), delete_alccontext))) {
283                         throw std::runtime_error("Audio: Global Initialization: Context Create");
284                 }
285
286                 if (!alcMakeContextCurrent(m_context.get()))
287                         throw std::runtime_error("Audio: Global Initialization: Context Current");
288
289                 alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);
290
291                 if (alGetError() != AL_NO_ERROR)
292                         throw std::runtime_error("Audio: Global Initialization: OpenAL Error");
293
294                 infostream << "Audio: Global Initialized: OpenAL " << alGetString(AL_VERSION)
295                         << ", using " << alcGetString(m_device.get(), ALC_DEVICE_SPECIFIER)
296                         << std::endl;
297         }
298
299         ~SoundManagerSingleton()
300         {
301                 infostream << "Audio: Global Deinitialized." << std::endl;
302         }
303 };
304
305 class OpenALSoundManager: public ISoundManager
306 {
307 private:
308         OnDemandSoundFetcher *m_fetcher;
309         ALCdevice *m_device;
310         ALCcontext *m_context;
311         int m_next_id;
312         std::unordered_map<std::string, std::vector<SoundBuffer*>> m_buffers;
313         std::unordered_map<int, PlayingSound*> m_sounds_playing;
314         struct FadeState {
315                 FadeState() = default;
316
317                 FadeState(float step, float current_gain, float target_gain):
318                         step(step),
319                         current_gain(current_gain),
320                         target_gain(target_gain) {}
321                 float step;
322                 float current_gain;
323                 float target_gain;
324         };
325
326         std::unordered_map<int, FadeState> m_sounds_fading;
327         float m_fade_delay;
328 public:
329         OpenALSoundManager(SoundManagerSingleton *smg, OnDemandSoundFetcher *fetcher):
330                 m_fetcher(fetcher),
331                 m_device(smg->m_device.get()),
332                 m_context(smg->m_context.get()),
333                 m_next_id(1),
334                 m_fade_delay(0)
335         {
336                 infostream << "Audio: Initialized: OpenAL " << std::endl;
337         }
338
339         ~OpenALSoundManager()
340         {
341                 infostream << "Audio: Deinitializing..." << std::endl;
342
343                 std::unordered_set<int> source_del_list;
344
345                 for (const auto &sp : m_sounds_playing)
346                         source_del_list.insert(sp.first);
347
348                 for (const auto &id : source_del_list)
349                         deleteSound(id);
350
351                 for (auto &buffer : m_buffers) {
352                         for (SoundBuffer *sb : buffer.second) {
353                                 delete sb;
354                         }
355                         buffer.second.clear();
356                 }
357                 m_buffers.clear();
358
359                 infostream << "Audio: Deinitialized." << std::endl;
360         }
361
362         void step(float dtime)
363         {
364                 doFades(dtime);
365         }
366
367         void addBuffer(const std::string &name, SoundBuffer *buf)
368         {
369                 std::unordered_map<std::string, std::vector<SoundBuffer*>>::iterator i =
370                                 m_buffers.find(name);
371                 if(i != m_buffers.end()){
372                         i->second.push_back(buf);
373                         return;
374                 }
375                 std::vector<SoundBuffer*> bufs;
376                 bufs.push_back(buf);
377                 m_buffers[name] = bufs;
378         }
379
380         SoundBuffer* getBuffer(const std::string &name)
381         {
382                 std::unordered_map<std::string, std::vector<SoundBuffer*>>::iterator i =
383                                 m_buffers.find(name);
384                 if(i == m_buffers.end())
385                         return nullptr;
386                 std::vector<SoundBuffer*> &bufs = i->second;
387                 int j = myrand() % bufs.size();
388                 return bufs[j];
389         }
390
391         PlayingSound* createPlayingSound(SoundBuffer *buf, bool loop,
392                         float volume, float pitch)
393         {
394                 infostream << "OpenALSoundManager: Creating playing sound" << std::endl;
395                 assert(buf);
396                 PlayingSound *sound = new PlayingSound;
397                 assert(sound);
398                 warn_if_error(alGetError(), "before createPlayingSound");
399                 alGenSources(1, &sound->source_id);
400                 alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
401                 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, true);
402                 alSource3f(sound->source_id, AL_POSITION, 0, 0, 0);
403                 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
404                 alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
405                 volume = std::fmax(0.0f, volume);
406                 alSourcef(sound->source_id, AL_GAIN, volume);
407                 alSourcef(sound->source_id, AL_PITCH, pitch);
408                 alSourcePlay(sound->source_id);
409                 warn_if_error(alGetError(), "createPlayingSound");
410                 return sound;
411         }
412
413         PlayingSound* createPlayingSoundAt(SoundBuffer *buf, bool loop,
414                         float volume, v3f pos, float pitch)
415         {
416                 infostream << "OpenALSoundManager: Creating positional playing sound"
417                                 << std::endl;
418                 assert(buf);
419                 PlayingSound *sound = new PlayingSound;
420                 assert(sound);
421                 warn_if_error(alGetError(), "before createPlayingSoundAt");
422                 alGenSources(1, &sound->source_id);
423                 alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
424                 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
425                 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
426                 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
427                 // Use alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED) and set reference
428                 // distance to clamp gain at <1 node distance, to avoid excessive
429                 // volume when closer
430                 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 10.0f);
431                 alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
432                 // Multiply by 3 to compensate for reducing AL_REFERENCE_DISTANCE from
433                 // the previous value of 30 to the new value of 10
434                 volume = std::fmax(0.0f, volume * 3.0f);
435                 alSourcef(sound->source_id, AL_GAIN, volume);
436                 alSourcef(sound->source_id, AL_PITCH, pitch);
437                 alSourcePlay(sound->source_id);
438                 warn_if_error(alGetError(), "createPlayingSoundAt");
439                 return sound;
440         }
441
442         int playSoundRaw(SoundBuffer *buf, bool loop, float volume, float pitch)
443         {
444                 assert(buf);
445                 PlayingSound *sound = createPlayingSound(buf, loop, volume, pitch);
446                 if(!sound)
447                         return -1;
448                 int id = m_next_id++;
449                 m_sounds_playing[id] = sound;
450                 return id;
451         }
452
453         int playSoundRawAt(SoundBuffer *buf, bool loop, float volume, const v3f &pos,
454                         float pitch)
455         {
456                 assert(buf);
457                 PlayingSound *sound = createPlayingSoundAt(buf, loop, volume, pos, pitch);
458                 if(!sound)
459                         return -1;
460                 int id = m_next_id++;
461                 m_sounds_playing[id] = sound;
462                 return id;
463         }
464
465         void deleteSound(int id)
466         {
467                 std::unordered_map<int, PlayingSound*>::iterator i = m_sounds_playing.find(id);
468                 if(i == m_sounds_playing.end())
469                         return;
470                 PlayingSound *sound = i->second;
471
472                 alDeleteSources(1, &sound->source_id);
473
474                 delete sound;
475                 m_sounds_playing.erase(id);
476         }
477
478         /* If buffer does not exist, consult the fetcher */
479         SoundBuffer* getFetchBuffer(const std::string &name)
480         {
481                 SoundBuffer *buf = getBuffer(name);
482                 if(buf)
483                         return buf;
484                 if(!m_fetcher)
485                         return nullptr;
486                 std::set<std::string> paths;
487                 std::set<std::string> datas;
488                 m_fetcher->fetchSounds(name, paths, datas);
489                 for (const std::string &path : paths) {
490                         loadSoundFile(name, path);
491                 }
492                 for (const std::string &data : datas) {
493                         loadSoundData(name, data);
494                 }
495                 return getBuffer(name);
496         }
497
498         // Remove stopped sounds
499         void maintain()
500         {
501                 verbosestream<<"OpenALSoundManager::maintain(): "
502                                 <<m_sounds_playing.size()<<" playing sounds, "
503                                 <<m_buffers.size()<<" sound names loaded"<<std::endl;
504                 std::unordered_set<int> del_list;
505                 for (const auto &sp : m_sounds_playing) {
506                         int id = sp.first;
507                         PlayingSound *sound = sp.second;
508                         // If not playing, remove it
509                         {
510                                 ALint state;
511                                 alGetSourcei(sound->source_id, AL_SOURCE_STATE, &state);
512                                 if(state != AL_PLAYING){
513                                         del_list.insert(id);
514                                 }
515                         }
516                 }
517                 if(!del_list.empty())
518                         verbosestream<<"OpenALSoundManager::maintain(): deleting "
519                                         <<del_list.size()<<" playing sounds"<<std::endl;
520                 for (int i : del_list) {
521                         deleteSound(i);
522                 }
523         }
524
525         /* Interface */
526
527         bool loadSoundFile(const std::string &name,
528                         const std::string &filepath)
529         {
530                 SoundBuffer *buf = load_ogg_from_file(filepath);
531                 if (buf)
532                         addBuffer(name, buf);
533                 return false;
534         }
535
536         bool loadSoundData(const std::string &name,
537                         const std::string &filedata)
538         {
539                 SoundBuffer *buf = load_ogg_from_buffer(filedata, name);
540                 if (buf)
541                         addBuffer(name, buf);
542                 return false;
543         }
544
545         void updateListener(const v3f &pos, const v3f &vel, const v3f &at, const v3f &up)
546         {
547                 alListener3f(AL_POSITION, pos.X, pos.Y, pos.Z);
548                 alListener3f(AL_VELOCITY, vel.X, vel.Y, vel.Z);
549                 ALfloat f[6];
550                 f3_set(f, at);
551                 f3_set(f+3, -up);
552                 alListenerfv(AL_ORIENTATION, f);
553                 warn_if_error(alGetError(), "updateListener");
554         }
555
556         void setListenerGain(float gain)
557         {
558                 alListenerf(AL_GAIN, gain);
559         }
560
561         int playSound(const std::string &name, bool loop, float volume, float fade, float pitch)
562         {
563                 maintain();
564                 if (name.empty())
565                         return 0;
566                 SoundBuffer *buf = getFetchBuffer(name);
567                 if(!buf){
568                         infostream << "OpenALSoundManager: \"" << name << "\" not found."
569                                         << std::endl;
570                         return -1;
571                 }
572                 int handle = -1;
573                 if (fade > 0) {
574                         handle = playSoundRaw(buf, loop, 0.0f, pitch);
575                         fadeSound(handle, fade, volume);
576                 } else {
577                         handle = playSoundRaw(buf, loop, volume, pitch);
578                 }
579                 return handle;
580         }
581
582         int playSoundAt(const std::string &name, bool loop, float volume, v3f pos, float pitch)
583         {
584                 maintain();
585                 if (name.empty())
586                         return 0;
587                 SoundBuffer *buf = getFetchBuffer(name);
588                 if(!buf){
589                         infostream << "OpenALSoundManager: \"" << name << "\" not found."
590                                         << std::endl;
591                         return -1;
592                 }
593                 return playSoundRawAt(buf, loop, volume, pos, pitch);
594         }
595
596         void stopSound(int sound)
597         {
598                 maintain();
599                 deleteSound(sound);
600         }
601
602         void fadeSound(int soundid, float step, float gain)
603         {
604                 m_sounds_fading[soundid] = FadeState(step, getSoundGain(soundid), gain);
605         }
606
607         void doFades(float dtime)
608         {
609                 m_fade_delay += dtime;
610
611                 if (m_fade_delay < 0.1f)
612                         return;
613
614                 float chkGain = 0;
615                 for (auto i = m_sounds_fading.begin();
616                                 i != m_sounds_fading.end();) {
617                         if (i->second.step < 0.f)
618                                 chkGain = -(i->second.current_gain);
619                         else
620                                 chkGain = i->second.current_gain;
621
622                         if (chkGain < i->second.target_gain) {
623                                 i->second.current_gain += (i->second.step * m_fade_delay);
624                                 i->second.current_gain = rangelim(i->second.current_gain, 0, 1);
625
626                                 updateSoundGain(i->first, i->second.current_gain);
627                                 ++i;
628                         } else {
629                                 if (i->second.target_gain <= 0.f)
630                                         stopSound(i->first);
631
632                                 m_sounds_fading.erase(i++);
633                         }
634                 }
635                 m_fade_delay = 0;
636         }
637
638         bool soundExists(int sound)
639         {
640                 maintain();
641                 return (m_sounds_playing.count(sound) != 0);
642         }
643
644         void updateSoundPosition(int id, v3f pos)
645         {
646                 auto i = m_sounds_playing.find(id);
647                 if (i == m_sounds_playing.end())
648                         return;
649                 PlayingSound *sound = i->second;
650
651                 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
652                 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
653                 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
654                 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0);
655         }
656
657         bool updateSoundGain(int id, float gain)
658         {
659                 auto i = m_sounds_playing.find(id);
660                 if (i == m_sounds_playing.end())
661                         return false;
662
663                 PlayingSound *sound = i->second;
664                 alSourcef(sound->source_id, AL_GAIN, gain);
665                 return true;
666         }
667
668         float getSoundGain(int id)
669         {
670                 auto i = m_sounds_playing.find(id);
671                 if (i == m_sounds_playing.end())
672                         return 0;
673
674                 PlayingSound *sound = i->second;
675                 ALfloat gain;
676                 alGetSourcef(sound->source_id, AL_GAIN, &gain);
677                 return gain;
678         }
679 };
680
681 std::shared_ptr<SoundManagerSingleton> createSoundManagerSingleton()
682 {
683         return std::shared_ptr<SoundManagerSingleton>(new SoundManagerSingleton());
684 }
685
686 ISoundManager *createOpenALSoundManager(SoundManagerSingleton *smg, OnDemandSoundFetcher *fetcher)
687 {
688         return new OpenALSoundManager(smg, fetcher);
689 };