]> git.lizzy.rs Git - dragonfireclient.git/blob - src/sound_openal.cpp
Code modernization: src/m* (part 3)
[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, float pitch)
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                 alSourcef(sound->source_id, AL_PITCH, pitch);
413                 alSourcePlay(sound->source_id);
414                 warn_if_error(alGetError(), "createPlayingSound");
415                 return sound;
416         }
417
418         PlayingSound* createPlayingSoundAt(SoundBuffer *buf, bool loop,
419                         float volume, v3f pos, float pitch)
420         {
421                 infostream<<"OpenALSoundManager: Creating positional playing sound"
422                                 <<std::endl;
423                 assert(buf);
424                 PlayingSound *sound = new PlayingSound;
425                 assert(sound);
426                 warn_if_error(alGetError(), "before createPlayingSoundAt");
427                 alGenSources(1, &sound->source_id);
428                 alSourcei(sound->source_id, AL_BUFFER, buf->buffer_id);
429                 alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false);
430                 alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z);
431                 alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0);
432                 alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0);
433                 alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
434                 volume = MYMAX(0.0, volume);
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, v3f pos, float pitch)
454         {
455                 assert(buf);
456                 PlayingSound *sound = createPlayingSoundAt(buf, loop, volume, pos, pitch);
457                 if(!sound)
458                         return -1;
459                 int id = m_next_id++;
460                 m_sounds_playing[id] = sound;
461                 return id;
462         }
463
464         void deleteSound(int id)
465         {
466                 std::unordered_map<int, PlayingSound*>::iterator i = m_sounds_playing.find(id);
467                 if(i == m_sounds_playing.end())
468                         return;
469                 PlayingSound *sound = i->second;
470
471                 alDeleteSources(1, &sound->source_id);
472
473                 delete sound;
474                 m_sounds_playing.erase(id);
475         }
476
477         /* If buffer does not exist, consult the fetcher */
478         SoundBuffer* getFetchBuffer(const std::string &name)
479         {
480                 SoundBuffer *buf = getBuffer(name);
481                 if(buf)
482                         return buf;
483                 if(!m_fetcher)
484                         return NULL;
485                 std::set<std::string> paths;
486                 std::set<std::string> datas;
487                 m_fetcher->fetchSounds(name, paths, datas);
488                 for(std::set<std::string>::iterator i = paths.begin();
489                                 i != paths.end(); ++i){
490                         loadSoundFile(name, *i);
491                 }
492                 for(std::set<std::string>::iterator i = datas.begin();
493                                 i != datas.end(); ++i){
494                         loadSoundData(name, *i);
495                 }
496                 return getBuffer(name);
497         }
498
499         // Remove stopped sounds
500         void maintain()
501         {
502                 verbosestream<<"OpenALSoundManager::maintain(): "
503                                 <<m_sounds_playing.size()<<" playing sounds, "
504                                 <<m_buffers.size()<<" sound names loaded"<<std::endl;
505                 std::set<int> del_list;
506                 for(std::unordered_map<int, PlayingSound*>::iterator i = m_sounds_playing.begin();
507                                 i != m_sounds_playing.end(); ++i) {
508                         int id = i->first;
509                         PlayingSound *sound = i->second;
510                         // If not playing, remove it
511                         {
512                                 ALint state;
513                                 alGetSourcei(sound->source_id, AL_SOURCE_STATE, &state);
514                                 if(state != AL_PLAYING){
515                                         del_list.insert(id);
516                                 }
517                         }
518                 }
519                 if(!del_list.empty())
520                         verbosestream<<"OpenALSoundManager::maintain(): deleting "
521                                         <<del_list.size()<<" playing sounds"<<std::endl;
522                 for(std::set<int>::iterator i = del_list.begin();
523                                 i != del_list.end(); ++i)
524                 {
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 == "")
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 == "")
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