]> git.lizzy.rs Git - dragonfireclient.git/blob - src/sound_openal.cpp
Fix OSX builds (closes #6289, fixes #6270) (#6306)
[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 <cassert>
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() = default;
279
280                 FadeState(float step, float current_gain, float target_gain):
281                         step(step),
282                         current_gain(current_gain),
283                         target_gain(target_gain) {}
284                 float step;
285                 float current_gain;
286                 float target_gain;
287         };
288
289         std::unordered_map<int, FadeState> m_sounds_fading;
290         float m_fade_delay;
291 public:
292         bool m_is_initialized;
293         OpenALSoundManager(OnDemandSoundFetcher *fetcher):
294                 m_fetcher(fetcher),
295                 m_device(NULL),
296                 m_context(NULL),
297                 m_next_id(1),
298                 m_fade_delay(0),
299                 m_is_initialized(false)
300         {
301                 ALCenum error = ALC_NO_ERROR;
302
303                 infostream<<"Audio: Initializing..."<<std::endl;
304
305                 m_device = alcOpenDevice(NULL);
306                 if(!m_device){
307                         infostream<<"Audio: No audio device available, audio system "
308                                 <<"not initialized"<<std::endl;
309                         return;
310                 }
311
312                 m_context = alcCreateContext(m_device, NULL);
313                 if(!m_context){
314                         error = alcGetError(m_device);
315                         infostream<<"Audio: Unable to initialize audio context, "
316                                         <<"aborting audio initialization ("<<alcErrorString(error)
317                                         <<")"<<std::endl;
318                         alcCloseDevice(m_device);
319                         m_device = NULL;
320                         return;
321                 }
322
323                 if(!alcMakeContextCurrent(m_context) ||
324                                 (error = alcGetError(m_device) != ALC_NO_ERROR))
325                 {
326                         infostream<<"Audio: Error setting audio context, aborting audio "
327                                         <<"initialization ("<<alcErrorString(error)<<")"<<std::endl;
328                         alcDestroyContext(m_context);
329                         m_context = NULL;
330                         alcCloseDevice(m_device);
331                         m_device = NULL;
332                         return;
333                 }
334
335                 alDistanceModel(AL_INVERSE_DISTANCE);
336
337                 infostream<<"Audio: Initialized: OpenAL "<<alGetString(AL_VERSION)
338                                 <<", using "<<alcGetString(m_device, ALC_DEVICE_SPECIFIER)
339                                 <<std::endl;
340
341                 m_is_initialized = true;
342         }
343
344         ~OpenALSoundManager()
345         {
346                 infostream<<"Audio: Deinitializing..."<<std::endl;
347                 // KABOOM!
348                 // TODO: Clear SoundBuffers
349                 alcMakeContextCurrent(NULL);
350                 alcDestroyContext(m_context);
351                 m_context = NULL;
352                 alcCloseDevice(m_device);
353                 m_device = NULL;
354
355                 for (auto &buffer : m_buffers) {
356                         for (SoundBuffer *sb : buffer.second) {
357                                 delete sb;
358                         }
359                         buffer.second.clear();
360                 }
361                 m_buffers.clear();
362                 infostream<<"Audio: Deinitialized."<<std::endl;
363         }
364
365         void step(float dtime)
366         {
367                 doFades(dtime);
368         }
369
370         void addBuffer(const std::string &name, SoundBuffer *buf)
371         {
372                 std::unordered_map<std::string, std::vector<SoundBuffer*>>::iterator i =
373                                 m_buffers.find(name);
374                 if(i != m_buffers.end()){
375                         i->second.push_back(buf);
376                         return;
377                 }
378                 std::vector<SoundBuffer*> bufs;
379                 bufs.push_back(buf);
380                 m_buffers[name] = bufs;
381         }
382
383         SoundBuffer* getBuffer(const std::string &name)
384         {
385                 std::unordered_map<std::string, std::vector<SoundBuffer*>>::iterator i =
386                                 m_buffers.find(name);
387                 if(i == m_buffers.end())
388                         return NULL;
389                 std::vector<SoundBuffer*> &bufs = i->second;
390                 int j = myrand() % bufs.size();
391                 return bufs[j];
392         }
393
394         PlayingSound* createPlayingSound(SoundBuffer *buf, bool loop,
395                         float volume, float pitch)
396         {
397                 infostream<<"OpenALSoundManager: Creating playing sound"<<std::endl;
398                 assert(buf);
399                 PlayingSound *sound = new PlayingSound;
400                 assert(sound);
401                 warn_if_error(alGetError(), "before createPlayingSound");
402                 alGenSources(1, &sound->source_id);
403                 alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
404                 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, true);
405                 alSource3f(sound->source_id, AL_POSITION, 0, 0, 0);
406                 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
407                 alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
408                 volume = MYMAX(0.0, volume);
409                 alSourcef(sound->source_id, AL_GAIN, volume);
410                 alSourcef(sound->source_id, AL_PITCH, pitch);
411                 alSourcePlay(sound->source_id);
412                 warn_if_error(alGetError(), "createPlayingSound");
413                 return sound;
414         }
415
416         PlayingSound* createPlayingSoundAt(SoundBuffer *buf, bool loop,
417                         float volume, v3f pos, float pitch)
418         {
419                 infostream<<"OpenALSoundManager: Creating positional playing sound"
420                                 <<std::endl;
421                 assert(buf);
422                 PlayingSound *sound = new PlayingSound;
423                 assert(sound);
424                 warn_if_error(alGetError(), "before createPlayingSoundAt");
425                 alGenSources(1, &sound->source_id);
426                 alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
427                 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
428                 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
429                 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
430                 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0);
431                 alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
432                 volume = MYMAX(0.0, volume);
433                 alSourcef(sound->source_id, AL_GAIN, volume);
434                 alSourcef(sound->source_id, AL_PITCH, pitch);
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, float pitch)
441         {
442                 assert(buf);
443                 PlayingSound *sound = createPlayingSound(buf, loop, volume, pitch);
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, const v3f &pos,
452                         float pitch)
453         {
454                 assert(buf);
455                 PlayingSound *sound = createPlayingSoundAt(buf, loop, volume, pos, pitch);
456                 if(!sound)
457                         return -1;
458                 int id = m_next_id++;
459                 m_sounds_playing[id] = sound;
460                 return id;
461         }
462
463         void deleteSound(int id)
464         {
465                 std::unordered_map<int, PlayingSound*>::iterator i = m_sounds_playing.find(id);
466                 if(i == m_sounds_playing.end())
467                         return;
468                 PlayingSound *sound = i->second;
469
470                 alDeleteSources(1, &sound->source_id);
471
472                 delete sound;
473                 m_sounds_playing.erase(id);
474         }
475
476         /* If buffer does not exist, consult the fetcher */
477         SoundBuffer* getFetchBuffer(const std::string &name)
478         {
479                 SoundBuffer *buf = getBuffer(name);
480                 if(buf)
481                         return buf;
482                 if(!m_fetcher)
483                         return NULL;
484                 std::set<std::string> paths;
485                 std::set<std::string> datas;
486                 m_fetcher->fetchSounds(name, paths, datas);
487                 for (const std::string &path : paths) {
488                         loadSoundFile(name, path);
489                 }
490                 for (const std::string &data : datas) {
491                         loadSoundData(name, data);
492                 }
493                 return getBuffer(name);
494         }
495
496         // Remove stopped sounds
497         void maintain()
498         {
499                 verbosestream<<"OpenALSoundManager::maintain(): "
500                                 <<m_sounds_playing.size()<<" playing sounds, "
501                                 <<m_buffers.size()<<" sound names loaded"<<std::endl;
502                 std::set<int> del_list;
503                 for (auto &sp : m_sounds_playing) {
504                         int id = sp.first;
505                         PlayingSound *sound = sp.second;
506                         // If not playing, remove it
507                         {
508                                 ALint state;
509                                 alGetSourcei(sound->source_id, AL_SOURCE_STATE, &state);
510                                 if(state != AL_PLAYING){
511                                         del_list.insert(id);
512                                 }
513                         }
514                 }
515                 if(!del_list.empty())
516                         verbosestream<<"OpenALSoundManager::maintain(): deleting "
517                                         <<del_list.size()<<" playing sounds"<<std::endl;
518                 for (int i : del_list) {
519                         deleteSound(i);
520                 }
521         }
522
523         /* Interface */
524
525         bool loadSoundFile(const std::string &name,
526                         const std::string &filepath)
527         {
528                 SoundBuffer *buf = load_ogg_from_file(filepath);
529                 if (buf)
530                         addBuffer(name, buf);
531                 return false;
532         }
533
534         bool loadSoundData(const std::string &name,
535                         const std::string &filedata)
536         {
537                 SoundBuffer *buf = load_ogg_from_buffer(filedata, name);
538                 if (buf)
539                         addBuffer(name, buf);
540                 return false;
541         }
542
543         void updateListener(v3f pos, v3f vel, v3f at, v3f up)
544         {
545                 m_listener_pos = pos;
546                 alListener3f(AL_POSITION, pos.X, pos.Y, pos.Z);
547                 alListener3f(AL_VELOCITY, vel.X, vel.Y, vel.Z);
548                 ALfloat f[6];
549                 f3_set(f, at);
550                 f3_set(f+3, -up);
551                 alListenerfv(AL_ORIENTATION, f);
552                 warn_if_error(alGetError(), "updateListener");
553         }
554
555         void setListenerGain(float gain)
556         {
557                 alListenerf(AL_GAIN, gain);
558         }
559
560         int playSound(const std::string &name, bool loop, float volume, float fade, float pitch)
561         {
562                 maintain();
563                 if (name.empty())
564                         return 0;
565                 SoundBuffer *buf = getFetchBuffer(name);
566                 if(!buf){
567                         infostream<<"OpenALSoundManager: \""<<name<<"\" not found."
568                                         <<std::endl;
569                         return -1;
570                 }
571                 int handle = -1;
572                 if (fade > 0) {
573                         handle = playSoundRaw(buf, loop, 0.0f, pitch);
574                         fadeSound(handle, fade, volume);
575                 } else {
576                         handle = playSoundRaw(buf, loop, volume, pitch);
577                 }
578                 return handle;
579         }
580
581         int playSoundAt(const std::string &name, bool loop, float volume, v3f pos, float pitch)
582         {
583                 maintain();
584                 if (name.empty())
585                         return 0;
586                 SoundBuffer *buf = getFetchBuffer(name);
587                 if(!buf){
588                         infostream<<"OpenALSoundManager: \""<<name<<"\" not found."
589                                         <<std::endl;
590                         return -1;
591                 }
592                 return playSoundRawAt(buf, loop, volume, pos, pitch);
593         }
594
595         void stopSound(int sound)
596         {
597                 maintain();
598                 deleteSound(sound);
599         }
600
601         void fadeSound(int soundid, float step, float gain)
602         {
603                 m_sounds_fading[soundid] = FadeState(step, getSoundGain(soundid), gain);
604         }
605
606         void doFades(float dtime)
607         {
608                 m_fade_delay += dtime;
609
610                 if (m_fade_delay < 0.1f)
611                         return;
612
613                 float chkGain = 0;
614                 for (std::unordered_map<int, FadeState>::iterator i = m_sounds_fading.begin();
615                                 i != m_sounds_fading.end();) {
616                         if (i->second.step < 0.f)
617                                 chkGain = -(i->second.current_gain);
618                         else
619                                 chkGain = i->second.current_gain;
620
621                         if (chkGain < i->second.target_gain) {
622                                 i->second.current_gain += (i->second.step * m_fade_delay);
623                                 i->second.current_gain = rangelim(i->second.current_gain, 0, 1);
624
625                                 updateSoundGain(i->first, i->second.current_gain);
626                                 ++i;
627                         } else {
628                                 if (i->second.target_gain <= 0.f)
629                                         stopSound(i->first);
630
631                                 m_sounds_fading.erase(i++);
632                         }
633                 }
634                 m_fade_delay = 0;
635         }
636
637         bool soundExists(int sound)
638         {
639                 maintain();
640                 return (m_sounds_playing.count(sound) != 0);
641         }
642
643         void updateSoundPosition(int id, v3f pos)
644         {
645                 std::unordered_map<int, PlayingSound*>::iterator i = m_sounds_playing.find(id);
646                 if (i == m_sounds_playing.end())
647                         return;
648                 PlayingSound *sound = i->second;
649
650                 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
651                 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
652                 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
653                 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0);
654         }
655
656         bool updateSoundGain(int id, float gain)
657         {
658                 std::unordered_map<int, PlayingSound*>::iterator i = m_sounds_playing.find(id);
659                 if (i == m_sounds_playing.end())
660                         return false;
661
662                 PlayingSound *sound = i->second;
663                 alSourcef(sound->source_id, AL_GAIN, gain);
664                 return true;
665         }
666
667         float getSoundGain(int id)
668         {
669                 std::unordered_map<int, PlayingSound*>::iterator i = m_sounds_playing.find(id);
670                 if (i == m_sounds_playing.end())
671                         return 0;
672
673                 PlayingSound *sound = i->second;
674                 ALfloat gain;
675                 alGetSourcef(sound->source_id, AL_GAIN, &gain);
676                 return gain;
677         }
678 };
679
680 ISoundManager *createOpenALSoundManager(OnDemandSoundFetcher *fetcher)
681 {
682         OpenALSoundManager *m = new OpenALSoundManager(fetcher);
683         if(m->m_is_initialized)
684                 return m;
685         delete m;
686         return NULL;
687 };
688