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